可见性
可见性:是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值
存在不可见问题的根本原因是由于缓存的存在,线程持有的是共享变量的副本,无法感知其他线程对于共享变量的更改,导致读取的值不是最新的。但是 final 修饰的变量是不可变的,就算有缓存,也不会存在不可见的问题
main 线程对 run 变量的修改对于 t 线程不可见,导致了 t 线程无法停止:
1 | static boolean run = true; //添加volatile |
原因:
- 初始状态, t 线程刚开始从主内存读取了 run 的值到工作内存
- 因为 t 线程要频繁从主内存中读取 run 的值,JIT 编译器会将 run 的值缓存至自己工作内存中的高速缓存中,减少对主存中 run 的访问,提高效率
- 1 秒之后,main 线程修改了 run 的值,并同步至主存,而 t 是从自己工作内存中的高速缓存中读取这个变量的值,结果永远是旧值
解决方案
加锁
1 | // main方法 |
某一个线程进入synchronized代码块前后,执行过程入如下:
a. 线程获得锁
b. 清空工作内存
c. 从主内存拷贝共享变量最新的值到工作内存成为副本
d. 执行代码
e. 将修改后的副本的值刷新回主内存中
f. 线程释放锁
volatile关键字
使用volatile关键字:
1 | private volatile boolean flag ; |
工作原理:
VolatileThread线程从主内存读取到数据放入其对应的工作内存
将flag的值更改为true,但是这个时候flag的值还没有写会主内存
此时main方法main方法读取到了flag的值为false
当VolatileThread线程将flag的值写回去后,失效其他线程对此变量副本
再次对flag进行操作的时候线程会从主内存读取最新的值,放入到工作内存中
总结: volatile保证不同线程对共享变量操作的可见性,也就是说一个线程修改了volatile修饰的变量,当修改写回主内存时,另外一个线程立即看到最新的值。
1 | ⚠️ volatile不保证原子性。 |
volatile与synchronized
volatile只能修饰实例变量和类变量,而synchronized可以修饰方法,以及代码块。
volatile保证数据的可见性,但是不保证原子性(多线程进行写操作,不保证线程安全),而synchronized是一种排他(互斥)的机制,