`

Java并发编程之volatile的理解

    博客分类:
  • Java
阅读更多

 Java并发编程之volatile关键字的理解 

        Java中每个线程都有自己的工作内存,类比于处理器的缓存,线程的工作内存中保存了被该线程使用到的变量的主内存的拷贝。线程读写变量都是直接在自己的工作内存中进行的,而何时刷新数据(指将修改的结果更新到主存或者把主存的变量读取覆盖掉工作内存中的值)是不确定的。

 

       volatile关键字是修饰字段的关键字,貌似是JDK1.5之后才有的,在多线程编程中,很大的几率会用到这个关键字,volatile修饰变量后该变量有这么一种效果:线程每一次读该变量都是直接从主存(JVM的主存)中读,而不是从线程的工作内存中;每一次写该变量都会同时写到主存中,而不仅仅是线程的工作内存中。因此一开头说的"何时刷新数据是不确定的"只适用于非volatile变量。

 

JVM对volatile变量有两个保证:

  1. 可见性。这个上面也大概解释了,就是某个线程改变了值,另一个线程立马就能读到改变后的值,容易理解。
  2. Happens-Before。有两点要说明:
  • 线程在写volatile变量时,若对一个普通变量的写在对该volatile变量的写之前,那么对该普通变量的写也将会被写到主存,而不仅仅是工作内存;线程在读volatile变量时,若对一个普通变量的读在对该volatile变量的读之后,那么对该普通变量的读将会先和主存同步,再读取,而不是直接从工作内存中读。例如
Java代码 
  1. Thread A:  
  2. object.nonVolatileVar = 1;  // stepA1  
  3. object.volatileVar = object.volatileVar + 1// stepA2  
  4.   
  5. Thread B:  
  6. int volatileVar = object.volatileVar; // stepB1  
  7. int nonVolatile = object.nonVolatileVar; // stepB2  

 线程A执行到stepA2,当要把volatileVar的新值写到主存时,nonVolatileVar的新值也会被刷到主存中;线程B执行到stepB1时,当要从主存中读object.volatileVar时,object.nonVolatileVar也会被一起读进工作内存,因此当线程 B执行到StepB2时,是可以拿到nonVolatileVar 的最新值的。这种特性其实蛮有用的:当一个线程有多个volatile变量时,可以根据这个特性减少volatile变量(通过变量的读、写顺序),可以达到和多个volatile变量同样的效果。

 

  • 对volatile变量的读写指令是不会被JVM重排序的。读/写之前或之后的其他指令可以重排序,但对volatile变量的读/写指令和其它指令的相对顺序是不会改变的。例如
Java代码 
  1. object.nonVolatile1 = 123;  //instruction1  
  2. object.nonVolatile2 = 456;  //instruction2  
  3. object.nonVolatile3 = 789// //instruction3  
  4.   
  5. object.volatile     = true//a volatile variable, //instruction4  
  6.   
  7. int value1 = sharedObject.nonVolatile4; //instruction5  
  8. int value2 = sharedObject.nonVolatile5;  //instruction6  
  9. int value3 = sharedObject.nonVolatile6;   //instruction7  

 由于JVM发现instruction1、instruction2、instruction3没有前后作用关系,因此jvm有可能会重排序这三条指令,instruction456也是如此,但是中间有个volatile变量的读。因此instruction123是不会被重排序到instruction4后面去的,同样instruction456也不会重排序到instruction4前面去的,他们的相对顺序不会变。

 

一个很常见的用volatile的例子就是单例模式(某种线程安全的写法):

 

Java代码 
  1. public class Singleton {  
  2.       
  3.     private volatile static Singleton instance;  
  4.       
  5.     public static Singleton getInstance() {  
  6.         if(instance == null) { //step1  
  7.             synchronized (Singleton.class) { //step2  
  8.                 if(instance == null) { //step3  
  9.                     instance = new Singleton(); // step4  
  10.                 }  
  11.             }  
  12.         }  
  13.         return instance;  
  14.     }  
  15.     private Singleton(){}  
  16. }  

  这里的isntance如果不用volatile修饰,那么这个单例就是非多线程安全的,知道synchronized有可见性保证的人可能会问:为什么这里用了synchronized还需要用volatile修饰?确实,这里两者都保证了可见性,但是这里用volatile不是因为可见性的原因,而是因为指令重排序的原因:首先要知道一点的就是new一个对象时有三步(伪码):

 

Java代码 
  1. memory = allocate();   //1:分配对象的内存空间  
  2. ctorInstance(memory);  //2:初始化对象  
  3. instance = memory;     //3:设置instance指向刚分配的内存地址  

 而这三条指令肯定都是同一个线程执行,根据intra-thread semantic(intra-thread semantics保证重排序不会改变单线程内的程序执行结果),这三条指令是可以重排序成下面这样的:

Java代码 
  1. memory = allocate();   //1:分配对象的内存空间  
  2. instance = memory;     //3:设置instance指向刚分配的内存地址  
  3. ctorInstance(memory);  //2:初始化对象  

 那么上面的单例就有问题了。假设不幸上述重排序发生了,那么初始化对象的线程正好设置了instance = memory(即instance已经不为null了)但是instance还没被初始化时,另一个线程跑到step1,发现instance不为null,然后直接把instance拿去用了,后面自然就会出现各种问题,因为对象根本还没被初始化。用了volatile修饰后,上面所说的重排序就被禁止了。

 

java.util.concurrent包下用到volatile的地方数不胜数,比如java.util.concurrent.FutureTask<T>中就有使用到volatile的happens-before原则::


 


 可以看到有两个变量state, callable都需要保证其可见性, 但是这里只用volatile修饰其中一个,而通过写的顺序来保证不被volatile修饰的那个变量的可见性。

 

书上看到的:“除了volatile外,synchronized和final也能实现可见性。synchronized的可见性:在离开synchronized代码块前,必须先把变量同步到主存中。final的可见性:被final修饰的字段在构造器中一旦完成初始化,并且构造器没有把this引用传递出去,那在其他线程中就能看到这个值”。

 

以上是个人结合书本的理解,如果不妥,欢迎指正。

1
1
分享到:
评论

相关推荐

    Java并发编程:volatile关键字解析

    Java并发编程:volatile关键字解析

    Java并发编程系列- volatile

    Java并发编程系列- volatile;Java并发编程系列- volatile;Java并发编程系列- volatile;Java并发编程系列- volatile;Java并发编程系列- volatile;

    Java 并发核心编程

    这篇指南主要是为帮助java多线程开发人员理解并发的核心概念以及如何应用这些理念。本文的主题是关于具有java语言风格的Thread、synchronized、volatile,以及J2SE5中新增的概念,如锁(Lock)、原子性(Atomics)、并发...

    Java并发编程实战

    1.1 并发简史 1.2 线程的优势 1.2.1 发挥多处理器的强大能力 1.2.2 建模的简单性 1.2.3 异步事件的简化处理 1.2.4 响应更灵敏的用户界面 1.3 线程带来的风险 1.3.1 安全性问题 1.3.2 活跃性问题 1.3.3 ...

    java并发编程

    , 本书通过帮助读者理解有关并发编程的模式及其利弊,向读者展示了如何更精确地使用Java平台的线程模型。, 这里,读者将通过使用java.lang.thread类、synchronized和volatile关键字,以及wait、notify和notifyall...

    Java并发编程的艺术.md

    《Java并发编程的艺术》笔记 第一章 并发编程的挑战 第二章 Java并发机制的底层实现原理 volatile的两条实现原则: 1. Lock前缀指令会引起处理器缓存回写到内存 2. 一个处理器的缓存回写到内存会导致其他...

    Java并发编程之volatile变量介绍

    主要介绍了Java并发编程之volatile变量介绍,volatile提供了弱同步机制,用来确保将变量更新通知到其它线程,需要的朋友可以参考下

    java并发编程与内存模型

    描述java并发编程原理 一.内存模型的相关概念 二.并发编程中的三个概念 三.Java内存模型 四..深入剖析volatile关键字 五.使用volatile关键字的场景

    Java并发编程:设计原则与模式(第二版)

    java并发方面的两大名著之一。读者将通过使用java.lang.thread类、synchronized和volatile关键字,以及wait、notify和notifyall方法,学习如何初始化、控制和协调并发操作。此外,本书还提供了有关并发编程的全方位...

    Java 并发编程实战

    1.1 并发简史 1.2 线程的优势 1.2.1 发挥多处理器的强大能力 1.2.2 建模的简单性 1.2.3 异步事件的简化处理 1.2.4 响应更灵敏的用户界面 1.3 线程带来的风险 1.3.1 安全性问题 1.3.2 活跃性问题 1.3.3 ...

    Java并发编程原理与实战

    深入理解volatile原理与使用.mp4 JDK5提供的原子类的操作以及实现原理.mp4 Lock接口认识与使用.mp4 手动实现一个可重入锁.mp4 AbstractQueuedSynchronizer(AQS)详解.mp4 使用AQS重写自己的锁.mp4 重入锁原理与演示....

    Java并发编程(5)volatile变量修饰符-意料之外

    Java并发编程(5)volatile变量修饰符—意料之外的问题(含代码)编程开发技术共6页.pdf.zip

    龙果 java并发编程原理实战

    龙果 java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四...

    【并发编程】volatile的原理我好像又懂了.pdf

    书籍:如《Java并发编程实战》、《Concurrency in C++》等。 官方文档:不同编程语言的官方文档通常会提供关于并发编程的指南和最佳实践。 社区和论坛:如Stack Overflow、Reddit等,可以提供实际问题的帮助和讨论。

    Java并发编程学习笔记

    7、并发工具类CountDownLatch 、CyclicBarrier和Semaphore底层实现原理 8、线程池原理和如何使用线程池 9、ThreadLocal 为什么会内存泄漏 10、Volatile底层实现原理 11、AQS源码分析 12、CAS原理分析和使用场景 13、...

    汪文君高并发编程实战视频资源下载.txt

    │ Java并发编程.png │ ppt+源码.rar │ 高并发编程第二阶段01讲、课程大纲及主要内容介绍.wmv │ 高并发编程第二阶段02讲、介绍四种Singleton方式的优缺点在多线程情况下.wmv │ 高并发编程第二阶段03讲、...

    java并发编程理论基础精讲

    本资源为您提供了关于 Java 并发编程理论基础的精讲,涵盖了多线程编程的核心概念、基本原理以及在 Java 中的应用。通过深入学习,您将建立坚实的并发编程基础,能够更好地理解和应对多线程编程中的挑战。 并发编程...

    深入分析java并发编程中volatile的实现原理

    主要介绍了深入分析java并发编程中Volatile的实现原理,涉及Volatile的官方定义,实现原理,使用优化等相关内容,具有一定参考价值,需要的朋友可以了解下。

    Java并发编程-volatile可见性详解

    主要介绍了Java并发编程-volatile可见性详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

Global site tag (gtag.js) - Google Analytics