Java,可见性和无限循环发生

时间:2012-01-07 08:26:58

标签: java concurrency thread-safety

我正在学习Java Concurrency in Practice并在那里解释为什么下面的代码段不好:

public class NoVisibility {
    private static boolean ready;
    private static int number;
    private static class ReaderThread extends Thread {

      public void run() {
      while (!ready) {
        Thread.yield();
        System.out.println(number);
      }
    }

    public static void main(String[] args) {
       new ReaderThread().start();
       number = 42;
       ready = true;
   }
}

此代码可能会打印0或永久循环。 虽然很容易理解为什么NoVisibility可以打印0而不是42(由于重新排序问题), 我对无限循环感到有点困惑。

在这段代码中,可能出现无限循环的实际情况是什么?

3 个答案:

答案 0 :(得分:8)

ready设置为true时,循环停止。主线程将ready设置为true。但由于ready字段不是易失性的,循环线程可能会继续看到缓存的值:false

volatile关键字保证读取volatile字段的所有线程实际上都会看到任何其他线程存储在此字段中的最后一个值。没有volatile,您就没有此保证。

答案 1 :(得分:0)

嗯,事实并非如此简单:当您在循环中打印时,您在输出流上进行同步,并且涉及所有内存屏障,因此您将实际退出。

现在,你不能依赖这样的行为,但这意味着如果你要证明这个问题,你应该格外小心:许多系统调用,特别是I / O,将涉及隐藏的同步,这会破坏一个特技。所以你最终会说,“那是baaaaad” - 并且无法证明这一点,这有点令人沮丧。

对于一个代码示例,请查看illustrating volatile : is this code thread-safe?(抱歉无耻的插件只是我在那里只有这个代码)。

答案 2 :(得分:0)

请参阅下面的代码,它介绍了x86上的无限循环。 试过jdk8和jdk7

package com.snippets;


public class SharedVariable {

    private static int  sharedVariable = 0;// declare as volatile to make it work
    public static void main(String[] args) throws InterruptedException {

        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sharedVariable = 1;
            }
        }).start();

        for(int i=0;i<1000;i++) {
            for(;;) {
                if(sharedVariable == 1) {
                    break;
                }
            }
        }
        System.out.println("Value of SharedVariable : " + sharedVariable);
    }

}
  

Trick不希望处理器进行重新排序而是制作   编译器进行一些引入可见性错误的优化。

如果您运行上面的代码,您将看到它无限期挂起,因为它永远不会看到更新的值sharedVariable。

要更正代码,请将sharedVariable声明为volatile。

为什么普通变量不起作用且上述程序挂起?

  1. sharedVariable未声明为volatile。
  2. 现在因为sharedVariable未被声明为volatile编译器优化代码。 它看到sharedVariable不会被改变,所以为什么我应该阅读 每次在循环中从内存中。这将使sharedVariable脱离循环。类似于下面的东西。

    for(int i=0i<1000;i++)/**compiler reorders sharedVariable
    as it is not declared as volatile
    and takes out the if condition out of the loop
    which is valid as compiler figures out that it not gonna  
    change sharedVariable is not going change **/
      if(sharedVariable != 1) {  
        for(;;) {}  
      }      
    }
    
  3. 在github上分享:https://github.com/lazysun/concurrency/blob/master/Concurrency/src/com/snippets/SharedVariable.java