public class GuardedBlock {
private boolean guard = false; private static void threadMessage(String message) { System.out.println(Thread.currentThread().getName() + ": " + message); } public static void main(String[] args) { GuardedBlock guardedBlock = new GuardedBlock(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); guardedBlock.guard = true; threadMessage("Set guard=true"); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { threadMessage("Start waiting"); while (!guardedBlock.guard) { //threadMessage("Still waiting..."); } threadMessage("Finally!"); } }); thread1.start(); thread2.start(); } }
我正在通过Java Essentials教程学习并发性。找到了守卫的方块并尝试对其进行测试。我无法理解的一件事。
虽然循环是无限的,但是如果您取消注释threadMessage行,则一切正常。为什么?
简短答案
您忘了声明guard为易失性布尔值。
guard
如果您将字段声明省略为volatile,则不会告诉JVM该字段可以被多线程看到,例如您的示例。
volatile
在这种情况下,的值guard将被读取一次,并且将导致无限循环。它将针对以下内容进行优化(无打印):
if(!guard) { while(true) { } }
现在为什么要System.out.println改变这种行为?因为writes是同步的,这迫使线程不缓存读取。
System.out.println
writes
这里粘贴了println一种PrintStream使用的方法的代码System.out.println:
println
PrintStream
public void println(String x) { synchronized (this) { print(x); newLine(); } }
和write方法:
write
private void write(String s) { try { synchronized (this) { ensureOpen(); textOut.write(s); textOut.flushBuffer(); charOut.flushBuffer(); if (autoFlush && (s.indexOf('\n') >= 0)) out.flush(); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } }
注意同步。