`

一个java volatile测试揭开的陷阱

阅读更多

 玩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 关键字实战java ...

    Java线程:volatile关键字

    volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。 正确使用 volatile 变量的条件是:对变量的写操作不依赖于当前值,以及该变量...

    java volatile 关键字 学习

    java volatile 关键字 学习

    Java volatile与AQS锁内存可见性

    从JUC中的AQS引入,讲解Java volatile与AQS锁内存可见性

    【Java面试题】volatile的作用

    【Java面试题】volatile的作用

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

    总结来说,`volatile`关键字是Java并发编程中一个非常重要的概念。它不仅解决了多线程环境下的可见性问题,还能够在一定程度上保证有序性。然而,它并不保证复合操作的原子性,因此在设计并发程序时,还需要根据具体...

    violate java-Java 之 volatile 超级详解

    Java中的`volatile`关键字是多线程编程中一个非常重要的概念,它用于修饰变量,确保在并发环境下,多个线程可以正确地共享和同步数据。本文将深入探讨`volatile`关键字的工作原理、特性以及如何使用它来解决多线程中...

    java陷阱常见面试题

    3. 解释什么是死锁,并给出一个简单的死锁示例。 4. 分析ArrayList和LinkedList在不同场景下的性能差异。 5. 在多线程环境下,如何正确使用synchronized关键字? 6. Java中如何实现线程间的通信? 7. 解释Java中的...

    Java volatile关键字详解.docx

    volatile

    【C语言】Volatile的陷阱

    volatile 关键字是 C 语言中一个非常重要的修饰符,但它的使用需要非常小心,以避免一些意想不到的结果。本文详细介绍了 volatile 关键字的使用注意事项和陷阱,以帮助开发者更好地理解和使用 volatile 关键字。

    一个简单的Java并发系统动态测试工具.zip

    Java并发系统动态测试工具是一个强大的资源,用于检查和分析多线程程序在运行时的行为。这个工具基于Java语言,能够帮助开发者深入理解并发程序的工作原理,发现并修复潜在的线程安全问题,如竞态条件、死锁和活锁等...

    Volatile的陷阱.pdf

    ` 表示rt_clock是一个只读的、不可修改的volatile变量,适用于表示由硬件定时器更新但其他代码不应修改的系统时钟。在中断服务程序中,应以`volatile unsigned int rt_clock;`的形式局部定义,以便能够读取最新的值...

    Java Volatile 变量详解及使用方法

    当一个线程修改了`volatile`变量的值,其他线程在读取这个变量时会获取到最新的值,而不是缓存中的旧值。这是通过Java内存模型(JMM)中的主内存与工作内存之间的交互来实现的,`volatile`变量的读写操作会强制刷新...

    java volatile关键字使用方法及注意事项

    1. **volatile的可见性**:当一个变量被声明为volatile后,每次对这个变量的修改都会立即刷新到主内存,其他线程在读取这个变量时会从主内存获取最新值,而不是使用各自工作内存中的副本。这样就避免了线程之间的...

    java volatile关键字作用及使用场景详解

    Java中的`volatile`关键字是一个非常重要的并发编程工具,它的主要作用是确保共享变量在多线程环境下的可见性和有序性。下面将详细解释`volatile`的关键特性、它如何解决并发问题以及相关的`happens-before`原则。 ...

    java里的volatile关键字详解

    3. 有序性:Java语言提供了volatile 和synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized是由“一个变量在同一个时刻只允许一条线程对其进行...

    一文精通Java中的volatile关键字

    Java中的`volatile`关键字是多线程编程中的一个重要概念,它的主要作用是确保共享变量的可见性和禁止指令重排序。本文将深入探讨`volatile`的关键特性、工作原理以及使用注意事项。 1. 可见性: `volatile`关键字...

Global site tag (gtag.js) - Google Analytics