基本问题
- JAVA中的多线程的基本问题就是安全问题。一方面是如何防止死锁发生,另一方面是如何保证同一资源在多个线程操作间和操作后还维持正确的状态,即资源的状态正确性。
- 资源的状态正确性可以由对资源操作的原子性,代码执行的顺序性,和资源的可见性来共同保证。
三个性质的定义
- 对资源操作的原子性,即定义一种操作(由一行或多行代码定义),在这个操作执行的任何时候都不可被中断,要么执行完成,要么不执行。
- 代码执行的顺序性,即防止编译器和处理器对代码进行重排序,典型的场景就是多线程中对两个变量的初始化顺序,在后面的代码中有对这两个变量的使用顺序有要求。
- 资源可见性,即资源(变量等)在内存中并非唯一一份,导致对资源的操作存在数据一致性问题。是由于java的线程内存模型中存在着工作内存和主内存,其工作内存每个线程私有一份并且其中的资源是主内存中资源的一个备份,线程直接操作工作内存的资源,之后再同步到主内存,当多个线程操作同一资源时,其本质是操作其工作内存中的资源,同时更改资源后再同步到主内存,会导致资源状态覆盖,也就是部分线程丢失更新。
三个性质的保证结果
- 保证了资源可见性,就是使得一个线程对资源的操作对其他线程可见,其本质是使得其他线程工作内存中的相同资源备份失效,强制从主内存再次拷贝最新数据以使用。
- 保证了原子性,就使得线程在一定粒度上的执行结果是绝对可以预见的,不可能存在意外的结果,执行即成功。
- 保证了代码的顺序性,就使得在代码底层执行的顺序和预期的一致,不会因为底层的自作聪明的优化而出现意料之外的问题。
- 只要保证这三个性质的必要性,就可以保证资源的最终状态正确性。
volatile与synchronized
- volatile 保证每次拿到最新的资源(变量),即保证的是在多线程中资源的可见性,以及部分顺序性,在被它修饰的资源(变量)的相邻的代码的顺序不会被重排序。
- synchronized 用于保证多线程之间的同步性,其实质是使得多线程在代码临界区以顺序执行,任何时间在此区域执行的线程只有一个,即任何时候只有一个线程访问和操作这个(多个)资源,也就不存在资源的状态正确性问题。
死锁
- 死锁即多个线程对已持有的资源不释放同时需求其他线程所持有的资源,而产生一种所有线程首尾循环持有和需求资源,并以期待需求的资源被释放而进入无限等待状态的情况。
- 死锁产生的本质是由于多个线程对多个资源的争夺产生的,或者说是线程对资源的使用不当产生的。需要从资源的使用方面解决。