同步如何帮助实现可变可见性?

时间:2013-06-18 09:25:27

标签: java multithreading synchronization

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;
   }
}

根据“Java Concurrency in Practice”,它可能会打印0,因为写入就绪可能会对读取器线程可见,然后写入数字或程序永远不会终止,因为它没有使用足够的同步。无法保证读取器线程可以看到主线程写入的ready和number的值。

怎么可能?程序将由一个线程顺序运行,它首先写入数字然后写入就绪变量。不是吗?这个程序怎么能永远循环呢?

2 个答案:

答案 0 :(得分:1)

无法保证通过在一个线程中更改变量,其他线程将看到这些更改的结果。从技术上讲,它们之间没有发生过关系,因此没有保证(在实践中,你几乎会一直看到这些变化)。

这就是线程可以永远运行的原因。

其次,为什么有时为0?

JLS

  

写入数据竞争中的一个线程,并在另一个线程中读取   例如,线程可能看起来不按顺序发生。

这意味着你的

number = 42;
ready = true;

可以按任何顺序发生。现在,它们更有可能按顺序出现,但再次没有保证。

你可以通过将变量更改为易失性来修复它,在这种情况下,读者总是可以看到写入,或者通过使代码变为一个关键部分的状态(参见本书)。一般来说,我觉得使用太多的volatile变量有点像黑客,所以你应该谨慎使用它们,比如线程“运行”变量。

public class NoVisibility {
    private static volatile boolean ready;
    private static volatile 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;
    }
}

答案 1 :(得分:0)

如果ready未标记为'volatile',则ReaderThread可能会检查其值(为0)。 它后来不再检查ready的值,因为它假设它没有改变。

volatile关键字指示编译器根本没有这样的假设,并且变量的值可能会在他的脚下发生变化。