玩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 ...
volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。 正确使用 volatile 变量的条件是:对变量的写操作不依赖于当前值,以及该变量...
java volatile 关键字 学习
从JUC中的AQS引入,讲解Java volatile与AQS锁内存可见性
【Java面试题】volatile的作用
总结来说,`volatile`关键字是Java并发编程中一个非常重要的概念。它不仅解决了多线程环境下的可见性问题,还能够在一定程度上保证有序性。然而,它并不保证复合操作的原子性,因此在设计并发程序时,还需要根据具体...
Java中的`volatile`关键字是多线程编程中一个非常重要的概念,它用于修饰变量,确保在并发环境下,多个线程可以正确地共享和同步数据。本文将深入探讨`volatile`关键字的工作原理、特性以及如何使用它来解决多线程中...
3. 解释什么是死锁,并给出一个简单的死锁示例。 4. 分析ArrayList和LinkedList在不同场景下的性能差异。 5. 在多线程环境下,如何正确使用synchronized关键字? 6. Java中如何实现线程间的通信? 7. 解释Java中的...
volatile
volatile 关键字是 C 语言中一个非常重要的修饰符,但它的使用需要非常小心,以避免一些意想不到的结果。本文详细介绍了 volatile 关键字的使用注意事项和陷阱,以帮助开发者更好地理解和使用 volatile 关键字。
Java并发系统动态测试工具是一个强大的资源,用于检查和分析多线程程序在运行时的行为。这个工具基于Java语言,能够帮助开发者深入理解并发程序的工作原理,发现并修复潜在的线程安全问题,如竞态条件、死锁和活锁等...
` 表示rt_clock是一个只读的、不可修改的volatile变量,适用于表示由硬件定时器更新但其他代码不应修改的系统时钟。在中断服务程序中,应以`volatile unsigned int rt_clock;`的形式局部定义,以便能够读取最新的值...
当一个线程修改了`volatile`变量的值,其他线程在读取这个变量时会获取到最新的值,而不是缓存中的旧值。这是通过Java内存模型(JMM)中的主内存与工作内存之间的交互来实现的,`volatile`变量的读写操作会强制刷新...
1. **volatile的可见性**:当一个变量被声明为volatile后,每次对这个变量的修改都会立即刷新到主内存,其他线程在读取这个变量时会从主内存获取最新值,而不是使用各自工作内存中的副本。这样就避免了线程之间的...
Java中的`volatile`关键字是一个非常重要的并发编程工具,它的主要作用是确保共享变量在多线程环境下的可见性和有序性。下面将详细解释`volatile`的关键特性、它如何解决并发问题以及相关的`happens-before`原则。 ...
3. 有序性:Java语言提供了volatile 和synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized是由“一个变量在同一个时刻只允许一条线程对其进行...
Java中的`volatile`关键字是多线程编程中的一个重要概念,它的主要作用是确保共享变量的可见性和禁止指令重排序。本文将深入探讨`volatile`的关键特性、工作原理以及使用注意事项。 1. 可见性: `volatile`关键字...