【问题标题】:Atomic statement ignored without volatile keyword没有 volatile 关键字的原子语句被忽略
【发布时间】:2012-10-11 12:05:15
【问题描述】:

java并发教程指出:

对于引用变量和大多数原始变量(除了 long 和 double 之外的所有类型),读取和写入都是原子的。
对于声明为 volatile 的所有变量(包括 long 和 double 变量),读写都是原子的。

我创建了一个简单的实验来测试这个

package experiment0;

public class Experiment0 {

    public static int i=9;

    public static void main(String[] args) {
        i=9;
        (new Thread(){
            public void run(){
                while(i==9){
                    //System.out.println("i==9");
                }
                System.out.println("\ni!=9");
            }
        }).start();
        (new Thread(){
            public void run(){
                try{
                    Thread.sleep(3000);
                    i=8;
                }catch(Exception e){

                }
            }
        }).start();
    }
}

当我运行这段代码时,程序永远不会被终止!为了让它工作,我要么取消注释第一个线程的while循环内的行,要么将i声明为volatile。 我错过了什么吗?文档是否错误,是否应该说在这种情况下所有原语都应声明为易失性?为什么写信给System.out“解决”了问题?是否给第二个线程足够的时间来更改i

【问题讨论】:

  • 在我的情况下两者都很好..
  • 您引用的手册部分与您的代码存在的问题并不真正相关。 “原子”写入并不意味着另一个线程将看到这些更改。要更详细地探索这一点,您可以做的一件事是查看编译器是否优化了读取,这就是您看不到更新的原因。
  • 原子意味着更新是不可分割的。您永远不会看到中间状态,只会看到旧值或新值。如果您只看到旧值,它仍然是原子的。
  • 有关为什么写入 System.out 可以解决问题的说明,请参阅Loop doesn't see changed value without a print statement

标签: java multithreading concurrency atomic volatile


【解决方案1】:

写入是原子的意味着写入是作为单个原子操作执行的。除了赋值之前或之后的值,没有其他线程可以看到其他内容。

例如,在 long 值的情况下,赋值不是原子的,因为它实际上是作为两个操作执行的:一个分配前 32 位,另一个分配最后 32 位。因此,另一个线程可能会在仅执行两个操作中的一个之后读取变量的值,因此看到的值既不是前值也不是后值,而是“中间”值。

您的程序演示的是,在没有任何同步的情况下写入变量会导致线程之间出现可见性问题。一个线程完成的写入操作对另一个线程不可见。这是由于每个处理器都有自己的缓存,并且在每次分配时都不会写入主存储器。为确保写入可见,您需要将变量设为 volatile,或同步对变量的访问,或使用 AtomicInteger。

【讨论】:

  • 哦,我明白了,谢谢!我以前从未听说过可见性概念。
  • @JB Nizet:现代处理器不会在硬件中实现缓存一致性吗?所以当 'i=8' 在一个处理器中执行,然后被另一个处理器读取时,硬件会刷新缓存并从内存中读取它。?
  • @vikky.rk 是的。但是,Java 内存模型并不要求架构是缓存一致的,并且允许 Java 虚拟机将循环 while (i==9) { } 优化为 if (i == 9) { while (true) { } }。由于此类优化,您仍然会在缓存一致的架构上遇到线程可见性问题。
猜你喜欢
  • 1970-01-01
  • 2015-01-31
  • 1970-01-01
  • 2017-09-22
  • 2015-08-13
  • 2014-03-04
  • 2016-02-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多