java 多线程 总结三

2023-11-05

本文转载至:http://blog.csdn.net/vking_wang/article/details/9952063

1、synchronized

把代码块声明为 synchronized,有两个重要后果,通常是指该代码具有 原子性(atomicity)和 可见性(visibility)

1.1 原子性

原子性意味着个时刻,只有一个线程能够执行一段代码,这段代码通过一个monitor object保护。从而防止多个线程在更新共享状态时相互冲突。


1.2 可见性

可见性则更为微妙,它要对付内存缓存和编译器优化的各种反常行为。它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 。

作用:如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。

原理:当对象获取锁时,它首先使自己的高速缓存无效,这样就可以保证直接从主内存中装入变量。 同样,在对象释放锁之前,它会刷新其高速缓存,强制使已做的任何更改都出现在主内存中。 这样,会保证在同一个锁上同步的两个线程看到在 synchronized 块内修改的变量的相同值。


一般来说,线程以某种不必让其他线程立即可以看到的方式(不管这些线程在寄存器中、在处理器特定的缓存中,还是通过指令重排或者其他编译器优化),不受缓存变量值的约束,但是如果开发人员使用了同步,那么运行库将确保某一线程对变量所做的更新先于对现有synchronized 块所进行的更新,当进入由同一监控器(lock)保护的另一个synchronized 块时,将立刻可以看到这些对变量所做的更新。类似的规则也存在于volatile变量上。

——volatile只保证可见性,不保证原子性!


1.3 何时要同步?

可见性同步的基本规则是在以下情况中必须同步: 

  1. 读取上一次可能是由另一个线程写入的变量 
  2. 写入下一次可能由另一个线程读取的变量

一致性同步:当修改多个相关值时,您想要其它线程原子地看到这组更改—— 要么看到全部更改,要么什么也看不到。

这适用于相关数据项(如粒子的位置和速率)和元数据项(如链表中包含的数据值和列表自身中的数据项的链)。


在某些情况中,您不必用同步来将数据从一个线程传递到另一个,因为 JVM 已经隐含地为您执行同步。这些情况包括:

  1. 由静态初始化器(在静态字段上或 static{} 块中的初始化器)
  2. 初始化数据时 
  3. 访问 final 字段时 ——final对象呢?
  4. 在创建线程之前创建对象时 
  5. 线程可以看见它将要处理的对象时


1.4 synchronize的限制

synchronized是不错,但它并不完美。它有一些功能性的限制:

  1. 它无法中断一个正在等候获得锁的线程;
  2. 也无法通过投票得到锁,如果不想等下去,也就没法得到锁;
  3. 同步还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行,多数情况下,这没问题(而且与异常处理交互得很好),但是,确实存在一些非块结构的锁定更合适的情况。


2、ReentrantLock

Java.util.concurrent.lock 中的Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。

ReentrantLock 类实现了Lock ,它拥有与synchronized 相同的并发性和内存语义,但是添加了类似锁投票定时锁等候可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)

  1. class Outputter1 {    
  2.     private Lock lock = new ReentrantLock();// 锁对象    
  3.   
  4.     public void output(String name) {           
  5.         lock.lock();      // 得到锁    
  6.   
  7.         try {    
  8.             for(int i = 0; i < name.length(); i++) {    
  9.                 System.out.print(name.charAt(i));    
  10.             }    
  11.         } finally {    
  12.             lock.unlock();// 释放锁    
  13.         }    
  14.     }    
  15. }    

区别:

需要注意的是,用sychronized修饰的方法或者语句块在代码执行完之后锁自动释放,而是用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在try内,释放锁放在finally内!!

3、读写锁ReadWriteLock

上例中展示的是和synchronized相同的功能,那Lock的优势在哪里?

例如一个类对其内部共享数据data提供了get()和set()方法,如果用synchronized,则代码如下:

  1. class syncData {        
  2.     private int data;// 共享数据        
  3.     public synchronized void set(int data) {    
  4.         System.out.println(Thread.currentThread().getName() + "准备写入数据");    
  5.         try {    
  6.             Thread.sleep(20);    
  7.         } catch (InterruptedException e) {    
  8.             e.printStackTrace();    
  9.         }    
  10.         this.data = data;    
  11.         System.out.println(Thread.currentThread().getName() + "写入" + this.data);    
  12.     }       
  13.     public synchronized  void get() {    
  14.         System.out.println(Thread.currentThread().getName() + "准备读取数据");    
  15.         try {    
  16.             Thread.sleep(20);    
  17.         } catch (InterruptedException e) {    
  18.             e.printStackTrace();    
  19.         }    
  20.         System.out.println(Thread.currentThread().getName() + "读取" + this.data);    
  21.     }    
  22. }    

然后写个测试类来用多个线程分别读写这个共享数据:

  1. public static void main(String[] args) {    
  2. //        final Data data = new Data();    
  3.           final syncData data = new syncData();    
  4. //        final RwLockData data = new RwLockData();    
  5.           
  6.         //写入  
  7.         for (int i = 0; i < 3; i++) {    
  8.             Thread t = new Thread(new Runnable() {    
  9.                 @Override  
  10.         public void run() {    
  11.                     for (int j = 0; j < 5; j++) {    
  12.                         data.set(new Random().nextInt(30));    
  13.                     }    
  14.                 }    
  15.             });  
  16.             t.setName("Thread-W" + i);  
  17.             t.start();  
  18.         }    
  19.         //读取  
  20.         for (int i = 0; i < 3; i++) {    
  21.             Thread t = new Thread(new Runnable() {    
  22.                 @Override  
  23.         public void run() {    
  24.                     for (int j = 0; j < 5; j++) {    
  25.                         data.get();    
  26.                     }    
  27.                 }    
  28.             });    
  29.             t.setName("Thread-R" + i);  
  30.             t.start();  
  31.         }    
  32.     }    

运行结果:

  1. Thread-W0准备写入数据  
  2. Thread-W0写入0  
  3. Thread-W0准备写入数据  
  4. Thread-W0写入1  
  5. Thread-R1准备读取数据  
  6. Thread-R1读取1  
  7. Thread-R1准备读取数据  
  8. Thread-R1读取1  
  9. Thread-R1准备读取数据  
  10. Thread-R1读取1  
  11. Thread-R1准备读取数据  
  12. Thread-R1读取1  
  13. Thread-R1准备读取数据  
  14. Thread-R1读取1  
  15. Thread-R2准备读取数据  
  16. Thread-R2读取1  
  17. Thread-R2准备读取数据  
  18. Thread-R2读取1  
  19. Thread-R2准备读取数据  
  20. Thread-R2读取1  
  21. Thread-R2准备读取数据  
  22. Thread-R2读取1  
  23. Thread-R2准备读取数据  
  24. Thread-R2读取1  
  25. Thread-R0准备读取数据 //R0和R2可以同时读取,不应该互斥!  
  26. Thread-R0读取1  
  27. Thread-R0准备读取数据  
  28. Thread-R0读取1  
  29. Thread-R0准备读取数据  
  30. Thread-R0读取1  
  31. Thread-R0准备读取数据  
  32. Thread-R0读取1  
  33. Thread-R0准备读取数据  
  34. Thread-R0读取1  
  35. Thread-W1准备写入数据  
  36. Thread-W1写入18  
  37. Thread-W1准备写入数据  
  38. Thread-W1写入16  
  39. Thread-W1准备写入数据  
  40. Thread-W1写入19  
  41. Thread-W1准备写入数据  
  42. Thread-W1写入21  
  43. Thread-W1准备写入数据  
  44. Thread-W1写入4  
  45. Thread-W2准备写入数据  
  46. Thread-W2写入10  
  47. Thread-W2准备写入数据  
  48. Thread-W2写入4  
  49. Thread-W2准备写入数据  
  50. Thread-W2写入1  
  51. Thread-W2准备写入数据  
  52. Thread-W2写入14  
  53. Thread-W2准备写入数据  
  54. Thread-W2写入2  
  55. Thread-W0准备写入数据  
  56. Thread-W0写入4  
  57. Thread-W0准备写入数据  
  58. Thread-W0写入20  
  59. Thread-W0准备写入数据  
  60. Thread-W0写入29  

现在一切都看起来很好!各个线程互不干扰!等等。。读取线程和写入线程互不干扰是正常的,但是两个读取线程是否需要互不干扰??

对!读取线程不应该互斥!

我们可以用读写锁ReadWriteLock实现:

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


  1. class Data {        
  2.     private int data;// 共享数据    
  3.     private ReadWriteLock rwl = new ReentrantReadWriteLock();       
  4.     public void set(int data) {    
  5.         rwl.writeLock().lock();// 取到写锁    
  6.         try {    
  7.             System.out.println(Thread.currentThread().getName() + "准备写入数据");    
  8.             try {    
  9.                 Thread.sleep(20);    
  10.             } catch (InterruptedException e) {    
  11.                 e.printStackTrace();    
  12.             }    
  13.             this.data = data;    
  14.             System.out.println(Thread.currentThread().getName() + "写入" + this.data);    
  15.         } finally {    
  16.             rwl.writeLock().unlock();// 释放写锁    
  17.         }    
  18.     }       
  19.   
  20.     public void get() {    
  21.         rwl.readLock().lock();// 取到读锁    
  22.         try {    
  23.             System.out.println(Thread.currentThread().getName() + "准备读取数据");    
  24.             try {    
  25.                 Thread.sleep(20);    
  26.             } catch (InterruptedException e) {    
  27.                 e.printStackTrace();    
  28.             }    
  29.             System.out.println(Thread.currentThread().getName() + "读取" + this.data);    
  30.         } finally {    
  31.             rwl.readLock().unlock();// 释放读锁    
  32.         }    
  33.     }    
  34. }    

测试结果:

  1. Thread-W1准备写入数据  
  2. Thread-W1写入9  
  3. Thread-W1准备写入数据  
  4. Thread-W1写入24  
  5. Thread-W1准备写入数据  
  6. Thread-W1写入12  
  7. Thread-W0准备写入数据  
  8. Thread-W0写入22  
  9. Thread-W0准备写入数据  
  10. Thread-W0写入15  
  11. Thread-W0准备写入数据  
  12. Thread-W0写入6  
  13. Thread-W0准备写入数据  
  14. Thread-W0写入13  
  15. Thread-W0准备写入数据  
  16. Thread-W0写入0  
  17. Thread-W2准备写入数据  
  18. Thread-W2写入23  
  19. Thread-W2准备写入数据  
  20. Thread-W2写入24  
  21. Thread-W2准备写入数据  
  22. Thread-W2写入24  
  23. Thread-W2准备写入数据  
  24. Thread-W2写入17  
  25. Thread-W2准备写入数据  
  26. Thread-W2写入11  
  27. Thread-R2准备读取数据  
  28. Thread-R1准备读取数据  
  29. Thread-R0准备读取数据  
  30. Thread-R0读取11  
  31. Thread-R1读取11  
  32. Thread-R2读取11  
  33. Thread-W1准备写入数据  
  34. Thread-W1写入18  
  35. Thread-W1准备写入数据  
  36. Thread-W1写入1  
  37. Thread-R0准备读取数据  
  38. Thread-R2准备读取数据  
  39. Thread-R1准备读取数据  
  40. Thread-R2读取1  
  41. Thread-R2准备读取数据  
  42. Thread-R1读取1  
  43. Thread-R0读取1  
  44. Thread-R1准备读取数据  
  45. Thread-R0准备读取数据  
  46. Thread-R0读取1  
  47. Thread-R2读取1  
  48. Thread-R2准备读取数据  
  49. Thread-R1读取1  
  50. Thread-R0准备读取数据  
  51. Thread-R1准备读取数据  
  52. Thread-R0读取1  
  53. Thread-R2读取1  
  54. Thread-R1读取1  
  55. Thread-R0准备读取数据  
  56. Thread-R1准备读取数据  
  57. Thread-R2准备读取数据  
  58. Thread-R1读取1  
  59. Thread-R2读取1  
  60. Thread-R0读取1  


与互斥锁定相比,读-写锁定允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程)


从理论上讲,与互斥锁定相比,使用读-写锁定所允许的并发性增强将带来更大的性能提高。

在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。——例如,某个最初用数据填充并且之后不经常对其进行修改的 collection,因为经常对其进行搜索(比如搜索某种目录),所以这样的 collection 是使用读-写锁定的理想候选者。


4、线程间通信Condition

Condition可以替代传统的线程间通信,await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll()。

——为什么方法名不直接叫wait()/notify()/nofityAll()?因为Object的这几个方法是final的,不可重写!


传统线程的通信方式,Condition都可以实现。

注意,Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。


Condition的强大之处在于它可以为多个线程间建立不同的Condition

看JDK文档中的一个例子:假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存put 线程和take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个Condition 实例来做到这一点。

——其实就是java.util.concurrent.ArrayBlockingQueue的功能


  1. class BoundedBuffer {  
  2.   final Lock lock = new ReentrantLock();          //锁对象  
  3.   final Condition notFull  = lock.newCondition(); //写线程锁  
  4.   final Condition notEmpty = lock.newCondition(); //读线程锁  
  5.   
  6.   final Object[] items = new Object[100];//缓存队列  
  7.   int putptr;  //写索引  
  8.   int takeptr; //读索引  
  9.   int count;   //队列中数据数目  
  10.   
  11.   //写  
  12.   public void put(Object x) throws InterruptedException {  
  13.     lock.lock(); //锁定  
  14.     try {  
  15.       // 如果队列满,则阻塞<写线程>  
  16.       while (count == items.length) {  
  17.         notFull.await();   
  18.       }  
  19.       // 写入队列,并更新写索引  
  20.       items[putptr] = x;   
  21.       if (++putptr == items.length) putptr = 0;   
  22.       ++count;  
  23.   
  24.       // 唤醒<读线程>  
  25.       notEmpty.signal();   
  26.     } finally {   
  27.       lock.unlock();//解除锁定   
  28.     }   
  29.   }  
  30.   
  31.   //读   
  32.   public Object take() throws InterruptedException {   
  33.     lock.lock(); //锁定   
  34.     try {  
  35.       // 如果队列空,则阻塞<读线程>  
  36.       while (count == 0) {  
  37.          notEmpty.await();  
  38.       }  
  39.   
  40.       //读取队列,并更新读索引  
  41.       Object x = items[takeptr];   
  42.       if (++takeptr == items.length) takeptr = 0;  
  43.       --count;  
  44.   
  45.       // 唤醒<写线程>  
  46.       notFull.signal();   
  47.       return x;   
  48.     } finally {   
  49.       lock.unlock();//解除锁定   
  50.     }   
  51.   }   


优点:

假设缓存队列中已经存满,那么阻塞的肯定是写线程,唤醒的肯定是读线程,相反,阻塞的肯定是读线程,唤醒的肯定是写线程。

那么假设只有一个Condition会有什么效果呢?缓存队列中已经存满,这个Lock不知道唤醒的是读线程还是写线程了,如果唤醒的是读线程,皆大欢喜,如果唤醒的是写线程,那么线程刚被唤醒,又被阻塞了,这时又去唤醒,这样就浪费了很多时间。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java 多线程 总结三 的相关文章

  • Java线程:volatile关键字

    本文转载至 http lavasoft blog 51cto com 62575 222076 Java线程 volatile关键字 Java 语言包含两种内在的同步机制 同步块 或方法 和 volatile 变量 这两种机制的提出都是为了
  • java并发包:读写锁

    本文转载至 http blog csdn net a910626 article details 51900954 ReadWriteLock是jdk5中提供的读写分离锁 读写分离锁可以有效的帮助减少锁竞争 以提升性能 用锁分离的机制来提升
  • Java线程:线程的同步-同步方法

    本文转载至 http lavasoft blog 51cto com 62575 221914 Java线程 线程的同步 同步方法 线程的同步是保证多线程安全访问竞争资源的一种手段 线程的同步是Java多线程编程的难点 往往开发者搞不清楚什
  • Java线程:线程的交互

    本文转载至 http lavasoft blog 51cto com 62575 99157 线程交互是比较复杂的问题 SCJP要求不很基础 给定一个场景 编写代码来恰当使用等待 通知和通知所有线程 一 线程交互的基础知识 SCJP所要求的
  • java 线程:概念与原理

    本文转载至 http lavasoft blog 51cto com 62575 99150 一 操作系统中线程和进程的概念 现在的操作系统是多任务操作系统 多线程是实现多任务的一种方式 进程是指一个内存中运行的应用程序 每个进程都有自己独
  • Java线程:线程栈模型与线程的变量

    本文转载至 http lavasoft blog 51cto com 62575 99152 要理解线程调度的原理 以及线程执行过程 必须理解线程栈模型 线程栈是指某时刻时内存中线程调度的栈信息 当前调用的方法总是位于栈顶 线程栈的内容是随
  • Java线程:新特征-障碍器

    本文转载至 http lavasoft blog 51cto com 62575 222738 Java5中 添加了障碍器类 为了适应一种新的设计需求 比如一个大型的任务 常常需要分配好多子任务去执行 只有当所有子任务都执行完成时候 才能执
  • java并发包:概论

    本文转载至 http blog csdn net a910626 article details 51900917 为什么要学习并发 今天和一哥们聊天 聊着聊着聊到钱的方面 当时我就说 全世界60亿人 要是每人给我一块钱那不就发财了啊 哥们
  • java 多线程 总结三

    本文转载至 http blog csdn net vking wang article details 9952063 1 synchronized 把代码块声明为 synchronized 有两个重要后果 通常是指该代码具有 原子性 at
  • Java线程:并发协作-死锁

    本文转载至 http lavasoft blog 51cto com 62575 222074 Java线程 并发协作 死锁 线程发生死锁可能性很小 即使看似可能发生死锁的代码 在运行时发生死锁的可能性也是小之又小 发生死锁的原因一般是两个
  • 线程管理之获取和设置线程信息

    获取和设置线程信息 Thread类的对象中保存了一些属性信息能够帮助我们来辨别每一个线程 知道它的状态 调整控制其优先级 这些属性是 ID 每个线程的独特标识 Name 线程的名称 Priority 线程对象的优先级 优先级别在1 10之间
  • Java线程:新特征-有返回值的线程

    本文转载至 http lavasoft blog 51cto com 62575 222082 在Java5之前 线程是没有返回值的 常常为了 有 返回值 破费周折 而且代码很不好写 或者干脆绕过这道坎 走别的路了 现在Java终于有可返回
  • Java线程:线程的调度-守护线程

    本文转载至 http lavasoft blog 51cto com 62575 221845 Java线程 线程的调度 守护线程 守护线程与普通线程写法上基本么啥区别 调用线程对象的方法setDaemon true 则可以将其设置为守护线
  • java 多线程 总结一

    首先讲一下进程和线程的区别 进程 每个进程都有独立的代码和数据空间 进程上下文 进程间的切换会有较大的开销 一个进程包含1 n个线程 线程 同一类线程共享代码和数据空间 每个线程有独立的运行栈和程序计数器 PC 线程切换开销小 线程和进程一
  • java并发包:生产者消费者模式

    本文转载至 http blog csdn net a910626 article details 51900974 生产者消费者模式是一个经典的多线程设计模式 它为多线程间的协作提供了良好的解决方案 在生产者消费者模式中 通常有两类线程 即
  • Java线程:线程状态的转换

    本文转载至 http lavasoft blog 51cto com 62575 99153 一 线程状态类型 1 新建状态 New 新创建了一个线程对象 2 就绪状态 Runnable 线程对象创建后 其他线程调用了该对象的start 方
  • Java线程:新特征-阻塞栈

    本文转载至 http lavasoft blog 51cto com 62575 222530 对于阻塞栈 与阻塞队列相似 不同点在于栈是 后入先出 的结构 每次操作的是栈顶 而队列是 先进先出 的结构 每次操作的是队列头 这里要特别说明一
  • Java线程:线程的调度-休眠

    本文转载至 http lavasoft blog 51cto com 62575 221790 Java线程 线程的调度 休眠 Java线程调度是Java多线程的核心 只有良好的调度 才能充分发挥系统的性能 提高程序的执行效率 这里要明确的
  • 线程管理之Thread类相关方法简介

    CurrentThread 静态方法 currentThread 方法可返回代码段正在被那个线程调用的信息 简单案列 打印main 方法 正在被那个线程调用 package com zzg thread import com zzg obj
  • java并发包:fork/join

    本文转载至 http blog csdn net a910626 article details 51900967 Fork Join框架是Java7提供了的一个用于并行执行任务的框架 是一个把大任务分割成若干个小任务 最终汇总每个小任务结

随机推荐

  • 三. go 常见数据结构实现原理之 silce

    目录 一 基础 几个小问题 1 题目一 2 题目二 3 题目三 4 数组和切片陷阱 二 Slice实现原理 切片的创建与底层结构 append 与切片的扩容 切片的值传递引用传递 切片再切片 特殊切片 切片的 Copy 总结 一 基础 为什
  • Path的Data生成归总

    Path的Data数据有三种生成方式 1 最简单的是用Expression Design 可以粘贴来自其它软件的矢量图形 导出时选择 文件 gt 导出 gt 导出属性 gt 格式 gt XAML Silverlight 画布 即可得到XAM
  • ES6中var let const三种变量(一文弄清楚)

    ES6中三种变量声明 ECMAScript 变量是松散类型的 变量可以用于保存任何类型的数据 有3个关键字可以声明变量 var const 和 let 其中 var在ECMAScript 的所有版本中都可以使用 而 const 和 let
  • 判断密码是否包含键盘连续字母

    新增内容为增加键盘列排序检测 原理 用两个与传入密码长度相等的一维数组 Row行数组 Column列数组 按密码顺序在二维键盘数组中查找每个字符 找到了则用 一维行列数组分别存放密码中每个字符的行号和列号 然后循环分析行号和列号是否满足二维
  • Alist V3 “全新版本“ 使用 安装/启动 教程!

    Introduction AList文档 nn ci 如果安装了Docker 请确保将其添加到系统的PATH变量中 您可以通过在终端中运行命令echo PATH来检查这一点 输出应该包括码头工人二进制文件的路径 通常是 usr bin 码头
  • RuntimeError: Pin memory thread exited unexpectedly 或 OSError: [Errno 9] Bad file descriptor 的解决方法

    遇到这几个错 大多是因为内存不足 我的解决方法 方案一 pin memory 不变 pin memory的值仍然为True 不需要改为False 而是把batch size 和 num worker的值分别调成 batch size 2 n
  • 10-20-010-简介-目录-Kylin目录详解

    文章目录 1 视界 1 Kylin二进制源码目录解析 2 HDFS 目录结构 2 1 cardinality 2 2 coprocessor 2 3 kylin job id 2 4 resources 2 5 jdbc resources
  • 基于vue构建lib + 类型声明文件

    构建lib 通过创建一个vue项目 实现自己的组件或工具类 然后创建一个index ts作为导出的入口文件 接下来就可以通过Vue CLIkais构建自己的lib了 index ts export Fns from utils fn 创建了
  • 基于STM32的阿里云物联网项目实战

    引言 之前自学了一些关于阿里云物联网项目的开发 收获颇丰 但是总感觉网上的东西太散了 需要自己去不停的收集整理 于是在项目结束后决心自己写一篇比较具有实用性的指导文档 需要声明的是本文档只适合像我一样的新人小白 路过的大佬不喜勿喷 如有疏漏
  • java开发实习生-面试

    redis常用命令 登录 redis cli p a 检查key是否存在 exists key 设置和获取一个键的值 set get 删除键对 del key 同时获取多个 mget linux常用命令 list 查看文件 cd 切换目录
  • 持续交付与敏捷开发的历史

    软件中的 持续交付 不仅仅是一个热门话题 软件的持续交付是软件开发实践和客户保留的圣杯 这也是DevOps今天是如此热门的概念的原因 要了解持续交付的重要性 你需要了解敏捷的历史以及敏捷方法的来源 敏捷软件开发的历史并不是从敏捷宣言开始的
  • Spark:failed to launch: nice -n 0 /opt/spark/bin/spark-class org.apache.spark.deploy.worker.

    查看真实的报错结果 bash 4 4 cat spark org apache spark deploy master Master 1 spark manager 77d4d75859 kbr5v out nohup can t exec
  • 2023-9-11 台阶-Nim游戏

    题目链接 台阶 Nim游戏 include
  • 七大排序算法详解,动图展示 +代码实现,老奶奶看了都直呼内行

    七种 基于比较 的排序 1 插入排序 元素少时插排最快 2 希尔排序 3 选择排序 4 堆排序 5 冒泡排序 6 快速排序 重点 7 归并排序 重点 排序总结 1 插入排序 元素少时插排最快 1 有序区间 黄色区间 无序区间 蓝色区间 每次
  • 人脸识别之目标追踪识别

    人脸识别之目标追踪识别 1 开发工具 Python版本 Anaconda 3 8环境 开发软件 Pycharm社区版 相关模块 sys模块 pypinyin模块 os模块 opencv contrib python 模块 opencv py
  • grafana改用https登录

    grafana添加HTTPS证书 安装 openssl yum install y openssl 创建 certificates openssl req x509 out server crt keyout server key newk
  • mysql--排序

    在项目开发时 为了使查询的数据结果满足用户的要求 通常会对查询出的数据进行上升或下降的排序 MySQL针对不同的开发需求提供两种排序的方式 分别为单字段排序和多字段排序 接下来将对这两种排序方式的语法及使用进行详细讲解 1 单字段排序 单字
  • Android 7.0新特性总结

    2016年8月22日 谷歌正式推送Android 7 0 Nougat 牛轧糖 正式版 他们还会三个月一次推送开发版 而曝光的消息看 第一个开发版就是Android 7 1 Android N主要新增了以下的新特性和优化 一 新的Notif
  • LeetCode_433. 最小基因变化

    题目链接 力扣 这道题是一道经典的BFS题型 我觉得可能会踩坑导致不能一次AC的地方有两处 一是bankSize可能为0 那么我们开辟一个记录数组的时候会报错 二是题目所说的 起始基因序列 start 默认是有效的 但是它并不一定会出现在基
  • java 多线程 总结三

    本文转载至 http blog csdn net vking wang article details 9952063 1 synchronized 把代码块声明为 synchronized 有两个重要后果 通常是指该代码具有 原子性 at