玩java多线程的,大多都知道volatile:它能保证变量的可见性,其它线程能看到其最新值,但不能用于实现线程安全的变量自增;再深入点的可能知道,它会限制指令重排序,volatile操作前的操作(包括普通变量的读写)不能重排到它之后,反之亦然。
基于上面的认识,我设计了下面的测试
package test.thread; public class TestVolatile { private volatile int n1=0;//volatile private int n2=0; public static void main(String[] a) { new TestVolatile().test(); new TestVolatile().test(); } public void test() { Thread t1 = new Thread() { public void run() { do{ // System.out.println("---"); }while(n2-n1<=0); System.out.println("n2>n1"); } }; Thread t2 = new Thread() { public void run() { for (;n1 < Integer.MAX_VALUE; ) { ++n1; ++n2; } System.out.println("stoped"); } }; t1.start(); t2.start(); } }
对于n1,n2,只有t2线程对其修改,并且n1总是先于n2自增,所以有:
n1在任何时刻都大于或等于n2
然后在t1线程里,根据表达式的顺序,先读取n2,再读取n1,因为都在增大,所以后读取的应该比先读取的大,并且n1是volatile的,能保证读到的是最新值,所以应该有
在t1线程里,表达式n2-n1<=0恒成立
但事实却并非如此,此测试会输出“n2>n1”(注意:需要让JVM在server模式下进行测试)
此问题困扰了我几个月,百思不得其解,最近这几天,在一篇深入分析volatile的文章(本文末尾有链接)里找到了答案
其实volatile并不保证所有情况都不进行重排序,像下面两种情况,是允许指令重排序的
1、普通变量的读/写操作,然后volatile变量的读操作
2、volatile变量的写操作,然后普通变量的读/写操作
再回过来看上面的测试,
++n1;可以看作是temp=n1;temp=temp+1;n1=temp;最后执行的肯定是一个写入操作,接着是++n2;是普通操作,按照上面的规则2,此处允许重排序,n2的写入若被排到了n1的写入之前,那么n1>=n2就不是恒成立的了;
不仅t2线程这出了问题,t1线程里也出了问题
n2-n1<=0,先是读取普通变量n2,然后是volatile变量n1的读操作,按照规则1,也是可以重排序的,如果先读取了n1,接着t2线程让n1、n2都增加了,t1然后再读了n2,那么完全有可能n2大于n1
至此问题已经分析清楚了,volatile并不严格保证指令不被重排序。
既然如此,这算不算是volatile的设计缺陷呢?
其实这不能算缺陷,因为该测试中,对volatile的使用本身就是不合适的(没加volatile的变量对其它线程的可见性本身就有问题,其值是不确定的),没必为一个错误的用法作出严格的保证
若想详细了解volatile,请参考此文:http://www.infoq.com/cn/articles/java-memory-model-4
在此感谢作者 程晓明,解开了我几个月来的困惑。
相关推荐
java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java ...
主要讲述java线程volatile关键字
java volatile 关键字 学习
从JUC中的AQS引入,讲解Java volatile与AQS锁内存可见性
【Java面试题】volatile的作用
Java并发编程:volatile关键字解析
Volatile的陷阱
violate java_Java 之 volatile 超级详解
主要介绍了Java Volatile 变量详解及使用方法的相关资料,需要的朋友可以参考下
java语言的volatile教程,java语言的volatile关键字到底怎么用
主要介绍了java volatile关键字使用方法及注意事项的相关资料,当一个变量被声明为 volatile 后,java 内存模型确保所有使用该变量的线程能看到相同的、一致的值。,需要的朋友可以参考下
Java并发编程系列- volatile;Java并发编程系列- volatile;Java并发编程系列- volatile;Java并发编程系列- volatile;Java并发编程系列- volatile;
在本文里我们给大家分享的是关于java volatile关键字作用及使用场景的相关知识点内容,需要的朋友们学习下。
在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候可以万事大吉。 Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 ...
volatile是java中的关键词之一,这篇文章主要给大家介绍了关于Java中volatile关键字的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
java里的volatile关键字详解
synchronized是阻塞式同步,在线程...这个实际对普通变量没有规定的,而针对volatile修饰的变量给Java虚拟机特殊的约定,线程对volatile变量的修改会立刻被其他线程所感知,即不会出现数据脏读,从而保证数据的可见性。