Java volatile使用详细说明-可见性的详细讲解

版权声明: https://blog.csdn.net/ttt_12345/article/details/83588418

上篇我们一起学习了同步的各种骚操作,本篇来来看下同步状语从句:齐名的挥发性,不管你听到的是同步的一种弱形式也好,还是听到的最轻量的同步机制,都是java的研究者给予挥发性关键字的美誉。

本文要点:

1,同步和挥发性的特性差异

2,使用易失性需要满足的条件

3,易失性特性在虚拟机层面的实现

4,易失性示例代码

1,同步和挥发性的特性差异

1.1, synchronized保证可见性和原子性,而volatile仅保证可见性。原子性大家应该都知道了,关于可见性,是指当一个线程修改了被挥动修饰的变量后,新值对于其他线程来说,是可以立即得知的原理见下文。

1.2, synchronized可能会造成线程的阻塞,而volatile不会阻塞;

1.3,当共享数据的改变依赖于该数据之前的值时,同步可以保证线程

安全性,而挥发性不行.volatile仅适用于共享变量作为独立的状态开关时的线程安全性,如标识完成,中断等的标记使用(根本原因是挥发性仅保证可见性)。

1.4, synchronized修饰方法或代码块,volatile修饰变量,且被挥发修饰的变量不会参加重排序;

在虚拟机的之前发生规则中,有一条是关于挥发性的,如下:

volatile变量规则(Volatile Variable Rule):对一个volatile变量的写操作先行发生于后面对这个变量的读操作,这里的“后面”同样是指时间上的先后顺序。 

而关于之前发生规则,也就是先行规则,是指java的内存模型中定义的两项操作之间的偏序关系,如果操作一个在乙之前发生,那么操作阿产生的影响应该能被操作乙观察到,影响包括很多,比如改变了共享变量的值,发送了消息,进行了方法调用以及返回了值等。对之前发生规则感兴趣的话,可以自行了解下java的内存模型的这块。

2,使用易失性需要满足的条件

2.1,该变量的修改不依赖变量的当前值或能确保只有单一线程能修改该变量值;

2.2,变量不需要和其他变量共同参与不变约束;

注:关于不变约束,简单的说就是1 + 2 = 3时,不管在顺序执行程序中,还在多线程执行程序中,1 + 2都应该等于3,而不应该等于其他。

2.3,访问变量时,没有其他的原因需要加锁;

3,易失性特性在虚拟机层面的实现

虚拟会保障以一种可预见的方式告知其他线程被挥发性修饰的变量的更新,即,如果被挥发性修饰的变量被一个线程甲更新,那么线程甲工作内存中更新后的值会直接刷到主内存中,当其他线程需要用到该变量时,发现该变量是被易失性修饰,即使其他线程的工作内存中有该变量的副本值,也会直接从主内存中加载该变量的值进行使用。作为对比,我们看下普通变量在各个线程中的使用方式,普通变量在多线程环境下,也会在各个线程的工作内存中保存着一份线程的副本,但各个线程在使用该普通变量时,仅会关注各自工作内存中的副本值,而不会主动进行数据的通讯和同步。

4,易失性示例代码

4.1,是否使用挥发性修饰的共享变量

如下图所示:

上面两个图代码,在服务端模式下运行时,会在打印出“下面终止运行”后也一直打印“运行中......”。

注:读者在自己电脑下运行时可能会在打印出 “下面终止运行” 便真的终止了,出现这种情况的原因是自己的虚拟机并非在服务器模式下运行的。

当如下图中所示,加了红线上放的挥发后,则会正常的终止打印了。

4.1,使用挥发性修饰的共享的变量自增

如下面两个图所示:

示例图已放公众号-up随想,此处未重复添加,欢迎搜索公众号了随想或扫描下方二维码关注查看:

打印结果如下:可见并不能正常的进行自增操作,按我们的预期计数值应该是100 * 100,但实际结果却并非如我们预期。

示例图已放公众号-up随想,此处未重复添加,欢迎搜索公众号了随想或扫描下方二维码关注查看:

本篇完。

欢迎大家关注我的公众号号“up随想”!!

猜你喜欢

转载自blog.csdn.net/ttt_12345/article/details/83588418