- 原子性
原子性是指一个操作是不可中断的. 即使在多线程环境下, 一个操作一旦开始, 不会被其他线程干扰. - 可见性
可见性是指当一个线程修改某个共享变量时, 其他线程是否能立即知道这个修改. - 有序性
在并发时, 程序的执行可能出现乱序. 给人的直观感觉就是: 写在前面的代码, 会在后面执行.
原因是程序执行时, 可能会进行指令重排, 重排后的指令与原指令顺序未必一致.
0.1. 哪些指令不能重排: Happen-Before 规则
- 程序顺序原则: 一个线程内保证语义的串行性.
- volatile 规则: volatile 变量的写, 先发生于读, 这保证了 volatile 变量的可见性
- 锁规则: 解锁 (unlock) 必然发生在随后的加锁 (lock) 前
- 传递性: A 先于 B, B 先于 C, 那么 A 必然先于 C
- 线程的 start() 方法先于它的每一个动作
- 线程的所有操作先于线程的终结 (Thread.join())
- 线程的中断 (interrupt()) 先于被中断线程的代码
- 对象的构造函数执行, 结束先于 finalize() 方法
1. volatile
volatile 保证被申明的变量的可见性, 读写的有序性, 但不能保证变量操作的原子性.
每次读取变量, 都要从共享内存中复制到工作内存中; 同样, 每次修改变量, 都要从工作内存复制到共享内存中.
Happen-Before 规则保证 volatile 修饰的变量, 写必定发生在读之前.
2. volatile vs static
volatile 修饰的变量在多线程情况下的可见性和有序性, 但不能保证原子性.
static 修饰的变量在多个实例中只有一份, 即多实例共享变量.
volatile 和 static 能同时修饰变量, 他们没有冲突.
3. volatile vs final
volatile 和 final 不能能同时修饰变量.
final 修饰的变量不可改变, 所以能保证线程安全, 但 volatile 不能.