多线程|关于可见性

多线程|关于可见性
文章图片

t线程中有一个while-true循环,我们试图通过在主方法中将run置为0,让t线程停下来,但是执行程序我们发现,并没有和我们预想的一样,t线程会停下来
多线程|关于可见性
文章图片


尝试分析这个过程
【多线程|关于可见性】初始状态,t线程刚开始从主内存读取了run的值到工作内存
多线程|关于可见性
文章图片


因为t线程要频繁的从主内存中读取run的值,JIT编译器会将run的值缓存至自己内存中的高速缓存中(底层是CPU中的缓存),减少对主存中run的访问,提高效率
多线程|关于可见性
文章图片

一秒之后,main线程修改了run的值,并同步至主内存,而t是从自己工作内存中的高速缓存中读取这个变量的值,结果依然是旧值
解决方案就是在run这个值加一个volatile关键词修饰,它可以用来修饰成员变量和静态成员变量,它可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值, 线程操作volatile变量都是直接操作主存
synchronized也可以完成这一工作
多线程|关于可见性
文章图片

多线程|关于可见性
文章图片

也和我们预想的一样停止了,说明synchronized也可以保持可见性,但是synchronized会创建监视器,属于重量级的锁,volatile就更轻量,推荐使用volatile。
可见性和原子性
上述的例子体现的就是可见性,它保证的是多个线程之间,一个线程对volatile变量的修改对另一个线程可见,不能保证原子性,仅用在一个写线程,多个线程读的情况,依然无法解决指令交错的情况发生。
多线程|关于可见性
文章图片

synchronized 语句块既可以保证代码块的原子性,也同时保证代码块内变量的可见性,但缺点是synchronized是属于重量级操作,性能相对更低。
但是如果在前边示例的死循环中加入System.out.println()会发现即使不加volatile修饰符,线程t也能正确看到对run变量的修改。

因为print方法加了synchronized关键字(当然println也加了,换行操作也会持有锁)
而synchronized方法也是能保证了该同步块中的变量的可见性的,所以下次stop从主存中读出false就跳出了while。
这里记录一下synchronized做的操作:
1、获得同步锁;
2、清空工作内存;
3、从主内存拷贝对象副本到工作内存;
4、执行代码(计算或者输出等);
5、刷新主内存数据;
6、释放同步锁。
总结一句话:sychronized可以保证变量的可见性





    推荐阅读