java中volatile的作用_java中volatile关键字的作用与用法,讲的很透彻

java中volatile的作用_java中volatile关键字的作用与用法,讲的很透彻volatile让变量每次在使用的时候,都从主存中取。而不是从各个线程的“工作内存”。volatile具有synchronized关键字的“可见性”,但是没有synchronized关键字的“并发正确性”,也就是说不保证线程执行的有序性。也就是说,volatile变量对于每次使用,线程都能得到当前volatile变量的最新值。但是volatile变量并不保证并发的正确性。=============…

大家好,又见面了,我是你们的朋友全栈君。

volatile让变量每次在使用的时候,都从主存中取。而不是从各个线程的“工作内存”。

volatile具有synchronized关键字的“可见性”,但是没有synchronized关键字的“并发正确性”,也就是说不保证线程执行的有序性。

也就是说,volatile变量对于每次使用,线程都能得到当前volatile变量的最新值。但是volatile变量并不保证并发的正确性。

=========================分割线1=================================

在Java内存模型中,有main memory,每个线程也有自己的memory (例如寄存器)。为了性能,一个线程会在自己的memory中保持要访问的变量的副本。这样就会出现同一个变量在某个瞬间,在一个线程的memory中的值可能与另外一个线程memory中的值,或者main memory中的值不一致的情况。

一个变量声明为volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory中。以下例子展现了volatile的作用:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 public class StoppableTask extendsThread {

2

3 private volatile booleanpleaseStop;

4

5

6 public voidrun() {

7

8 while (!pleaseStop) {

9

10 //do some stuff…

11

12 }

13

14 }

15

16

17 public voidtellMeToStop() {

18

19 pleaseStop = true;

20

21 }

22

23 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

假如pleaseStop没有被声明为volatile,线程执行run的时候检查的是自己的副本,就不能及时得知其他线程已经调用tellMeToStop()修改了pleaseStop的值。

Volatile一般情况下不能代替sychronized,因为volatile不能保证操作的原子性,即使只是i++,实际上也是由多个原子操作组成:read i; inc; write i,假如多个线程同时执行i++,volatile只能保证他们操作的i是同一块内存,但依然可能出现写入脏数据的情况。如果配合Java 5增加的atomic wrapper classes,对它们的increase之类的操作就不需要sychronized。

Reference:

http://www.javamex.com/tutorials/synchronization_volatile.shtml

http://www.javamex.com/tutorials/synchronization_volatile_java_5.shtml

http://www.ibm.com/developerworks/cn/java/j-jtp06197.html

=========================分割线2=================================

恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。volatile是变量修饰符,而synchronized则作用于一段代码或方法;看如下三句get代码:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 inti1;

2 int geti1() {returni1;}

3 volatile inti2;

4 intgeti2()

5 {returni2;}

6 inti3;

7 synchronized int geti3() {returni3;}

8   geti1()

48304ba5e6f9fe08f3fa1abda7d326ab.png

得到存储在当前线程中i1的数值。多个线程有多个i1变量拷贝,而且这些i1之间可以互不相同。换句话说,另一个线程可能已经改变了它线程内的i1值,而这个值可以和当前线程中的i1值不相同。事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程可以有它自己的变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的i1值是1,线程1里的i1值是2,线程2里的i1值是3——这在线程1和线程2都改变了它们各自的i1值,而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。

geti2()得到的是“主”内存区域的i2数值。用volatile修饰后的变量不允许有不同于“主”内存区域的变量拷贝。换句话说,一个变量经

volatile修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。理所当然的,volatile修饰的变量存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。

既然volatile关键字已经实现了线程间数据同步,又要

synchronized干什么呢?呵呵,它们之间有两点不同。首先,synchronized获得并释放监视器——如果两个线程使用了同一个对象锁,监视器能强制保证代码块同时只被一个线程所执行——这是众所周知的事实。但是,synchronized也同步内存:事实上,synchronized在“

主”内存区域同步整个线程的内存。因此,执行geti3()方法做了如下几步:

1. 线程请求获得监视this对象的对象锁(假设未被锁,否则线程等待直到锁释放)

2. 线程内存的数据被消除,从“主”内存区域中读入(Java虚拟机能优化此步。。。[后面的不知道怎么表达,汗])

3. 代码块被执行

4. 对于变量的任何改变现在可以安全地写到“主”内存区域中(不过geti3()方法不会改变变量值)

5. 线程释放监视this对象的对象锁

因此volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。显然synchronized要比volatile消耗更多资源。

=========================分割线3=================================

volatile关键字相信了解Java多线程的读者都很清楚它的作用。volatile关键字用于声明简单类型变量,如int、float、

boolean等数据类型。如果这些简单数据类型声明为volatile,对它们的操作就会变成原子级别的。但这有一定的限制。例如,下面的例子中的n就不是原子级别的:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 packagemythread;

2

3 public class JoinThread extendsThread

4 {

5 public static volatile int n = 0;

6 public voidrun()

7 {

8 for ( int i = 0 ; i < 10 ; i ++)

9 try

10 {

11 n = n + 1;

12 sleep( 3 ); //为了使运行结果更随机,延迟3毫秒

13

14 }

15 catch(Exception e)

16 {

17 }

18 }

19

20 public static void main(String[] args) throwsException

21 {

22

23 Thread threads[] = new Thread[ 100];

24 for ( int i = 0 ; i < threads.length; i ++)

25 //建立100个线程

26 threads[i] = newJoinThread();

27 for ( int i = 0 ; i < threads.length; i ++)

28 //运行刚才建立的100个线程

29 threads[i].start();

30 for ( int i = 0 ; i < threads.length; i ++)

31 //100个线程都执行完后继续

32 threads[i].join();

33 System.out.println( ” n= ” +JoinThread.n);

34 }

35 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

如果对n的操作是原子级别的,最后输出的结果应该为n=1000,而在执行上面积代码时,很多时侯输出的n都小于1000,这说明n=n+1不是原子级别的操作。原因是声明为volatile的简单变量如果当前值由该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式都不是原子操作:

n  =  n  +   1 ;

n ++ ;

如果要想使这种情况变成原子操作,需要使用synchronized关键字,如上的代码可以改成如下的形式:

48304ba5e6f9fe08f3fa1abda7d326ab.png

packagemythread;

public class JoinThread extendsThread

{

public static int n = 0;

public static synchronized voidinc()

{

n ++;

}

public voidrun()

{

for ( int i = 0 ; i < 10 ; i ++)

try{

inc(); //n = n + 1 改成了 inc();

sleep( 3 ); //为了使运行结果更随机,延迟3毫秒

}

catch(Exception e)

{

}

}

public static void main(String[] args) throwsException

{

Thread threads[] = new Thread[ 100];

for ( int i = 0 ; i < threads.length; i ++)

//建立100个线程

threads[i] = newJoinThread();

for ( int i = 0 ; i < threads.length; i ++)

//运行刚才建立的100个线程

threads[i].start();

for ( int i = 0 ; i < threads.length; i ++)

//100个线程都执行完后继续

threads[i].join();

System.out.println( ” n= ” +JoinThread.n);

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

上面的代码将n=n+1改成了inc(),其中inc方法使用了synchronized关键字进行方法同步。因此,在使用volatile关键字时要慎重,并不是只要简单类型变量使用volatile修饰,对这个变量的所有操作都是原来操作,当变量的值由自身的上一个决定时,如n=n+1、n++ 等,volatile关键字将失效,只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的,如n = m + 1,这个就是原级别的。所以在使用volatile关键时一定要谨慎,如果自己没有把握,可以使用synchronized来代替volatile。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/139926.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • JVM优化之优化常用参数和工具

    JVM优化之优化常用参数和工具JVM优化之优化常用参数和工具内容提要jvm运行参数和参数设置jvm内存模型jmap命令的使用以及通过MAT工具分析定位分析内存溢出的方法jstack的使用visualJVM工具使用为什么要优化JVM1.生产环境需要承载更多的并发要求,对底层的优化能显著提升性能,节约成本2.测试和生产环境的不同可能导致我们无法实时了解具体性能问题,我们需要借助对JVM了解分析问题所在。…

    2022年5月5日
    45
  • 音频数字化简单原理「建议收藏」

    音频数字化简单原理     从字面上来说,数字化(Digital)就是以数字来表示,例如用数字去记录一张桌子的长宽尺寸,各木料间的角度,这就是一种数字化。跟数位常常一起被提到的字是模拟(Analog/Analogue),模拟的意思就是用一种相似的东西去表达,例如将桌子用传统相机将三视图拍下来,就是一种模拟的记录方式。两个概念:1、分贝(dB):声波振幅的度量单位,非

    2022年4月17日
    60
  • FPGA实现的SPI协议(二)—-基于SPI接口的FLASH芯片M25P16的使用「建议收藏」

    FPGA实现的SPI协议(二)—-基于SPI接口的FLASH芯片M25P16的使用「建议收藏」用flash芯片M25P16来验证下SPI的具体实现吧。

    2022年10月15日
    0
  • FLAG_ACTIVITY_CLEAR_TOP和singleTask的区别

    FLAG_ACTIVITY_CLEAR_TOP和singleTask的区别Activity的Flags的设置,可以让Activity的运行具有一些特殊的特性,比如有些可以产生和启动模式相同或相似效果的,还有比如Activity在非前台的时候,也不会保存后台的历史列表中。本文重点分析FLAG_ACTIVITY_CLEAR_TOP,也简单介绍一下其它几个常用的Flag以及使用场景FLAG_ACTIVITY_NEW_TASK将Activity指定为singleTas…

    2022年7月17日
    20
  • 如何用51单片机控制步进电机运动「建议收藏」

    如何用51单片机控制步进电机运动「建议收藏」本来接触单片机挺久了的,但是一直只是停留在非常初级的认识阶段,本科的时候上过几门课,但是从来没有自己捣鼓过单片机,这次突然来了兴趣,感觉一下子学到了好多东西,在这里好好整理一下。这篇文章只适合于入门阶段的小白阅读,高手请绕道。12年年初的时候购买了一套普中科技的“单片机开发试验仪”,好多次想好好学学,结果每一次都半途而废,主要原因还是周围的人都不会用,有问题都不知道找谁问,结果锁到箱子里一直到现在。

    2022年6月1日
    25
  • EasyExcel,让excel导入导出更加简单

    点击上方“全栈程序员社区”,星标公众号 重磅干货,第一时间送达 作者:风雨兼程 jianshu.com/p/8f3defdc76d4 EasyExcel 在做excel导入导出的时…

    2021年6月27日
    147

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号