多线程程序常考虑三种性质:原子性、可见性、有序性。
//线程1执行的代码 int i = 0; i = 10; //线程2执行的代码 j = i;
当线程1执行完i=10时,其过程是:将i的初始值读入高速缓存中,然后讲高速缓存中 i 的值改为10,再将i的新值刷入内存中;
若没有可见性,则i的新值未刷入主存时,线程2执行,此时线程2读取内存中的 i 的值为0。(线程1修改了 i 值,线程2没有立即看到)
3. 有序性:
程序执行的顺序按照代码中的先后顺序执行。
为了提高程序效率,编译器会对代码进行优化,将没有依赖关系的指令执行先后顺序进行重排,使指令执行顺序与代码先后顺序不同,但保 证最终的结果一致。虽然指令重排对单线程没有影响,但会影响多线程并发执行的正确性。
4. JAVA中实现:
原子性是通过同步(Synchronized或Lock锁机制)来实现;
可见性是通过volatile来实现(保证变量修改后立即写入内存);
有序性是通过同步或volatile来实现。
5. volatile的特性与作用:
1. volatile修饰变量后的特性:
(1)保证了多个线程对变量操作的可见性,即:一个线程修改了变量值,其他线程立即可见。(保证变量安全,不保证线程安全)
(2)禁止进行指令重排。(防止编译器自动优化,对代码顺序的指令重新排序)
2. volatile保证变量可见性:
(1)使用volatile修饰的变量,在一个线程中被修改后会强制立即写入内存(不经过线程工作内存,即线程中的寄存器或堆栈空间);
(2)使用volatile修饰的变量,当有线程修改其值后,其他线程的工作内存中该值无效;
(3)使用volatile修饰的变量,所有线程必须从内存读取变量值(不经过线程工作内存,即该线程的寄存器或堆栈空间)。
3. volatile不保证变量原子性:
某些情况下,volatile可以实现变量原子性,如:long和double都是64位宽,32位操作系统对这种类型变量的读取分两部分,第一次读取32位,第二次读取剩下的32位。读取操作不是原子性的,当用volatile修饰(volatile long)时,可以将操作变为原子性操作。
4. volatile保证变量有序性:
使用volatile修饰的变量,编译器不能对该变量所在指令重排(例如:该指令之前的指令集合相互可以重排,但不能重排到该指令后面)