volatile的用法

来源:互联网 发布:mac c4d r17安装教程 编辑:程序博客网 时间:2024/06/07 11:00

参考资料:Java多线程发展简史

volatile在Java中用于保证可见性,但这么讲还是过于抽象。发现这篇文章里关于volatile的例子非常好,于是抄过来了

public class Volatile {    public static void main(String[] args) {        final Volatile volObj = new Volatile();        Thread t2 = new Thread() {            public void run() {                while (true) {                    volObj.check();                }            }        };        t2.start();        Thread t1 = new Thread() {            public void run() {                while (true) {                    volObj.swap();                }            }        };        t1.start();    }    boolean boolValue;// use volatile to print "WTF!"    public void check() {        if (boolValue == !boolValue)            System.out.println("WTF!");    }    public void swap() {        try {            Thread.sleep(100);        } catch (InterruptedException e) {            e.printStackTrace();        }        boolValue = !boolValue;    }}

代码中存在两个线程,一个线程通过一个死循环不断在变换boolValue的取值;另一个线程每100毫秒执行“boolValue==!boolValue”,这行代码会取两次boolValue,可以想象的是,有一定概率会出现这两次取boolValue结果不一致的情况,那么这个时候就会打印“WTF!”。

但是,上面的情况是对boolValue使用volatile修饰保证其可见性的情况下出现的,如果不对boolValue使用volatile修饰,运行时就一次不会出现(起码在我的电脑上)打印“WTF!”的情形,换句话说,这反而是不太正常的,我无法猜测JVM做了什么操作,基本上唯一可以确定的是,没有用volatile修饰的时候,boolValue在获取的时候,并不能总取到最真实的值。

以上是原文中的分析。其实这里的分析还是有些小问题的,作者的意思是,使用 volatile 修饰 boolValue,会打印 “WTF”;不使用 volatile 修饰 boolValue,不会出现 “WTF”,认为这种现象不正常。

其实恰恰相反,这里的输出完全符合我们对 volatile 的预期,是正常现象。分析如下:

首先,check() 方法中 boolValue == !boolValue 这一句不是原子操作。简单地讲,不妨认为它至少包含两次读 boolValue 值的过程。

另外,看下图

如果有 volatile 修饰 boolValue,则各个线程中的 var1, var2 均应与主存中的保持一致。在这个例子中就是,t1,t2线程中的boolValue均与主存中的一致。考虑以下执行过程(这里只是一种可能的上下文切换过程)

  1. t1 从主存中读 boolValue 值, 假定这时 boolValue 为 true
  2. t2 修改了主存中的 boolValue 值, 这时 boolValue 变成 false
  3. t1 再次从主存中读 boolValue 值,这时取到的值是 false
  4. t1 执行 == 操作进行判断, 显然 true == !false

所以会打印 “WTF”

反过来,如果没有 volatile 修饰 boolValue,则上述过程很可能变成

  1. t1 从自己的内存副本中读 boolValue 值,假定这里 boolValue 为 true
  2. t2 修改线程内存副本中的 boolValue 值,这里的 boolValue 是变成什么值并不重要 (注意,这两个 boolValue 是不同的副本,所以互不影响)
  3. t1 再次从自己的内存副本中读 boolValue,这时取到的值是 true
  4. t1 执行 == 操作进行判断,显然 true != !true

所以这次不会打印 “WTF”

0 0
原创粉丝点击