摘要:但在多线程环境中就可能出现问题如下面代码线程语句语句线程线程中的语句和语句并没有数据依赖关系,所以可能会进行指令重排序,先去执行语句,而这时线程会以为线程已经执行完而去执行这样就导致程序出错。
经常会听到volatile这个关键字,但没有深入的去了解过它,今天好好的整理一下
要谈volatile,我们先谈谈它的老大哥synchronized
一.synchronized
并发编程中最重要的三个特性是什么?原子性,可见性,有序性。只要有一个不能保证,就有可能导致程序的运行错误,我们熟知的synchronized就能保障原子性,可见性,有序性,因为synchronized能保障任意一个时刻只有一个线程执行该代码块,自然就不存在原子性的问题,又因为在释放锁之前会将变量的修改刷新到主存中,因此保证可见性,又因为每一时刻只有一个线程在执行代码,相当于让线程顺序执行同步代码,所以也可以保证有序性。所以synchronized可保证原子性,可见性,有序性
二.volatile
volatile可以说是一个稍弱的同步机制,因为它可以保障可见性和有序性,不能保障原子性
1.volatile可见性
先看下图,java中所有的变量都存储在主内存区,在多线程环境中,还有自己的工作内存,线程操作变量时是从主存中拷贝变量到工作内存操作,这样很容易产生"脏读"的问题。synchronized解决此问题的方法上面也谈到了,volatile是如何保证的呢?当一个变量被volatile修饰时,它会保证每次被修改的值会被立刻更新到主存,而且当缓存1对某个变量进行修改时,其它线程的工作内存中的该变量的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效),缓存2发现自己的变量无效后会等待缓存行对应的主存地址被更新后从主存中读取最新值,这样就保证了可见性
2.volatile有序性
说到volatile的有序性就要提到指令重排序(Instruction Reorder),什么是指令重排序?这是JVM为了提高代码的执行效率对代码进行的优化,它不能保证代码先后顺序执行的一致,但可以保证执行的结果和顺序执行的结构一致。但在多线程环境中就可能出现问题,如下面代码
//线程1 func1(); 语句1 boolean flag = true; 语句2 //线程2 while (!flag){ sleep(); } func2();
线程1中的语句1和语句2并没有数据依赖关系,所以可能会进行指令重排序,先去执行语句2,而这时线程2会以为线程1已经执行完func1()而去执行func2(),这样就导致程序出错。
volatile关键字修饰的变量能禁止指令重排序,这样就在一定程度上保证了有序性
应用:
(1)状态标记量
//线程1 func1(); 语句1 boolean volatile flag = true; 语句2 //线程2 while (!flag){ sleep(); } func2();
这是对上面程序的改进,避免了程序出错
(2)单例模式中的双重校验锁
public class Singleton3 { private Singleton3(){} private static volatile Singleton3 instance = null; public static Singleton3 getInstance() { if(instance == null){ synchronized (Singleton3.class){ instance = new Singleton3(); } } return instance; } }
我们看到已经有synchronizd来保证线程同步,为什么还需要volatile来修饰变量instance呢?
原因在 instance = new Singleton3();这句,JVM实际上对它进行了如下操作
(1)给instance分配内存 (2)调用Singleton3的构造函数完成初始化成员变量 (3)将instance指向内存分配空间
但由于JVM存在指令重排序优化,执行的顺序可能就为1-3-2,就可能发生当3执行完另一个线程以为它已经执行完了,抢占当前线程,这时instance非null但没有初始化,所以另一个线程和以后的线程都会直接返回该没有初始化的instance,从而出现错误。当有volatile修饰变量instance,就可以禁止指令重排序,从而避免出错。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/69079.html
摘要:前半句是指线程内表现为串行的语义,后半句是指指令重排序现象和工作内存和主内存同步延迟现象。关于内存模型的讲解请参考死磕同步系列之。目前国内市面上的关于内存屏障的讲解基本不会超过这三篇文章,包括相关书籍中的介绍。问题 (1)volatile是如何保证可见性的? (2)volatile是如何禁止重排序的? (3)volatile的实现原理? (4)volatile的缺陷? 简介 volatile...
摘要:前半句是指线程内表现为串行的语义,后半句是指指令重排序现象和工作内存和主内存同步延迟现象。关于内存模型的讲解请参考死磕同步系列之。目前国内市面上的关于内存屏障的讲解基本不会超过这三篇文章,包括相关书籍中的介绍。问题 (1)volatile是如何保证可见性的? (2)volatile是如何禁止重排序的? (3)volatile的实现原理? (4)volatile的缺陷? 简介 volatile...
摘要:前半句是指线程内表现为串行的语义,后半句是指指令重排序现象和工作内存和主内存同步延迟现象。关于内存模型的讲解请参考死磕同步系列之。目前国内市面上的关于内存屏障的讲解基本不会超过这三篇文章,包括相关书籍中的介绍。问题 (1)volatile是如何保证可见性的? (2)volatile是如何禁止重排序的? (3)volatile的实现原理? (4)volatile的缺陷? 简介 volatile...
摘要:最近在看多线程相关,看到这篇来自大神关于关键字的讲解感觉非常详细易懂,特此转载一下。如果对增加声明则所有线程对的写都会立即刷新到主存中,而且所有对的读也都直接从主存中去读。 最近在看java多线程相关,看到这篇来自大神Jakob Jenkov关于Volatile关键字的讲解感觉非常详细易懂,特此转载一下。原文链接:http://tutorials.jenkov.com/j... 内存可...
摘要:数据结构重要成员变量代表整个哈希表。科普,解决多线程并行情况下使用锁造成性能损耗的一种机制,操作包含三个操作数内存位置预期原值和新值。 ConcurrenHashMap 。下面分享一下我对ConcurrentHashMap 的理解,主要用于个人备忘。如果有不对,请批评。 HashMap严重的勾起了我对HashMap家族的好奇心,下面分享一下我对ConcurrentHashMap 的理解...
阅读 1860·2021-11-22 15:24
阅读 1316·2021-11-12 10:36
阅读 3219·2021-09-28 09:36
阅读 1846·2021-09-02 15:15
阅读 2760·2019-08-30 15:54
阅读 2400·2019-08-30 11:02
阅读 2399·2019-08-29 13:52
阅读 3548·2019-08-26 11:53