词条信息

admin
admin
超级管理员
最近编辑者 发短消息   

相关词条

热门词条

更多>>
一个宽带装两个路由器怎么设置?
很多朋友可能存这种情况,家里房间比较多,之前已经安装了一个无线路由器,但是在某些房间信号很差,想再增加一个...
宽带升级到200兆了,路由器和网线是不是需要更换?
事实上,200兆宽带需要支持千兆网络传输速率的硬件。尽管200兆与千兆相差很大,但目前网络传输速率只有百兆和千...
2019年电子商务发展4大主流趋势
随着在线零售业的竞争日趋激烈,企业和卖家只有积极利用科技趋势才能保住增长势头,立于不败之地。据市场研究公司 S...
超1亿人朋友圈“仅三天可见”
你有多少个真正称得上朋友的人?前段时间微信创始人、腾讯公司高级副总裁张小龙在年度演讲里说起关于朋友圈的一件事:...
半夜总是醒了又睡睡了又醒怎么办?
现在是一个全民缺觉的时代,越来越多人因为加班、情感、焦虑等问题困扰着自己的失眠。急剧下降的睡眠质量,同样也会影...
解决网 >>所属分类 >> 程序开发    JAVA   

深入理解Java中的volatile关键字

标签: volatile Java

顶[0] 发表评论(0) 编辑词条

  本文就围绕volatile展开,主要介绍volatile的用法、volatile的原理,以及volatile是如何提供可见性和有序性保障的等。


  volatile这个关键字,不仅仅在Java语言中有,在很多语言中都有的,而且其用法和语义也都是不尽相同的。尤其在C语言、C++以及Java中,都有volatile关键字。都可以用来声明变量或者对象。下面简单来介绍一下Java语言中的volatile关键字。


目录

volatile的用法编辑本段回目录


  volatile通常被比喻成"轻量级的synchronized",也是Java并发编程中比较重要的一个关键字。和synchronized不同,volatile是一个变量修饰符,只能用来修饰变量。无法修饰方法及代码块等。


  volatile的用法比较简单,只需要在声明一个可能被多线程同时访问的变量时,使用volatile修饰就可以了。


publicclassSingleton{

privatevolatilestaticSingleton singleton;

privateSingleton(){}

publicstaticSingleton getSingleton(){

if(singleton == null) {

synchronized(Singleton.class) {

if(singleton == null) {

singleton = newSingleton();

}

}

}

returnsingleton;

}


}


  如以上代码,是一个比较典型的使用双重锁校验的形式实现单例的,其中使用volatile关键字修饰可能被多个线程同时访问到的singleton。


volatile的原理编辑本段回目录


  在再有人问你Java内存模型是什么,就把这篇文章发给他中我们曾经介绍过,为了提高处理器的执行速度,在处理器和内存之间增加了多级缓存来提升。但是由于引入了多级缓存,就存在缓存数据不一致问题。


  但是,对于volatile变量,当对volatile变量进行写操作的时候,JVM会向处理器发送一条lock前缀的指令,将这个缓存中的变量回写到系统主存中。


  但是就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题,所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议


  缓存一致性协议:每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。


  所以,如果一个变量被volatile所修饰的话,在每次数据变化之后,其值都会被强制刷入主存。而其他处理器的缓存由于遵守了缓存一致性协议,也会把这个变量的值从主存加载到自己的缓存中。这就保证了一个volatile在并发编程中,其值在多个缓存中是可见的。


volatile与可见性编辑本段回目录


  可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。


  我们在再有人问你Java内存模型是什么,就把这篇文章发给他中分析过:Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。所以,就可能出现线程1改了某个变量的值,但是线程2不可见的情况。


  前面的关于volatile的原理中介绍过了,Java中的volatile关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每次是用之前都从主内存刷新。因此,可以使用volatile来保证多线程操作时变量的可见性。


volatile与有序性编辑本段回目录


  有序性即程序执行的顺序按照代码的先后顺序执行。


  我们在再有人问你Java内存模型是什么,就把这篇文章发给他中分析过:除了引入了时间片以外,由于处理器优化和指令重排等,CPU还可能对输入代码进行乱序执行,比如load->add->save 有可能被优化成load->save->add 。这就是可能存在有序性问题。


  而volatile除了可以保证数据的可见性之外,还有一个强大的功能,那就是他可以禁止指令重排优化等。


  普通的变量仅仅会保证在该方法的执行过程中所依赖的赋值结果的地方都能获得正确的结果,而不能保证变量的赋值操作的顺序与程序代码中的执行顺序一致。


  volatile可以禁止指令重排,这就保证了代码的程序会严格按照代码的先后顺序执行。这就保证了有序性。被volatile修饰的变量的操作,会严格按照代码顺序执行,load->add->save 的执行顺序就是:load、add、save。


volatile与原子性编辑本段回目录


  原子性是指一个操作是不可中断的,要全部执行完成,要不就都不执行。


  我们在Java的并发编程中的多线程问题到底是怎么回事儿?中分析过:线程是CPU调度的基本单位。CPU有时间片的概念,会根据不同的调度算法进行线程调度。当一个线程获得时间片之后开始执行,在时间片耗尽之后,就会失去CPU使用权。所以在多线程场景下,由于时间片在线程间轮换,就会发生原子性问题。


  在上一篇文章中,我们介绍synchronized的时候,提到过,为了保证原子性,需要通过字节码指令monitorenter和monitorexit,但是volatile和这两个指令之间是没有任何关系的。


  所以,volatile是不能保证原子性的。


  在以下两个场景中可以使用volatile来代替synchronized:


   1、运算结果并不依赖变量的当前值,或者能够确保只有单一的线程会修改变量的值。


   2、变量不需要与其他状态变量共同参与不变约束。


  除以上场景外,都需要使用其他方式来保证原子性,如synchronized或者concurrent包。


  我们来看一下volatile和原子性的例子:


publicclassTest{
publicvolatileintinc = 0;
publicvoidincrease() {
inc++;
}
publicstaticvoidmain(String[] args) {
final Test test = newTest();
for( inti= 0;i< 10;i++){
newThread(){
publicvoidrun() {
for( intj= 0;j< 1000;j++)
test.increase();
};
}.start();
}
while(Thread.activeCount()> 1) //保证前面的线程都执行完
Thread. yield();
System. out.println(test.inc);
}
}


  以上代码比较简单,就是创建10个线程,然后分别执行1000次i++操作。正常情况下,程序的输出结果应该是10000,但是,多次执行的结果都小于10000。这其实就是volatile无法满足原子性的原因。


  为什么会出现这种情况呢,那就是因为虽然volatile可以保证inc在多个线程之间的可见性。但是无法inc++的原子性。


总结与思考编辑本段回目录


  我们介绍过了volatile关键字和synchronized关键字。现在我们知道,synchronized可以保证原子性、有序性和可见性。而volatile却只能保证有序性和可见性。


  那么,我们再来看一下双重校验锁实现的单例,已经使用了synchronized,为什么还需要volatile?


publicclassSingleton{
privatevolatilestaticSingleton singleton;
privateSingleton(){}
publicstaticSingleton getSingleton(){
if(singleton == null) {
synchronized(Singleton.class) {
if(singleton == null) {
singleton = newSingleton();
}
}
}
returnsingleton;
}
}

 

 

附件列表


按字母顺序浏览:A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

→我们致力于为广大网民解决所遇到的各种电脑技术问题
 如果您认为本词条还有待完善,请 编辑词条

上一篇剖析 Android 架构组件之 ViewModel
下一篇C语言

0
1. 本站部分内容来自互联网,如有任何版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
2. 本站内容仅供参考,如果您需要解决具体问题,建议您咨询相关领域专业人士。
3. 如果您没有找到需要的百科词条,您可以到百科问答提问或创建词条,等待高手解答。

关于本词条的提问

查看全部/我要提问>>