即使我修改了锁变量,为什么我会得到一个无限循环?

Prz*_*iak 7 java multithreading

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我通过java essentials教程学习并发.得到防护块并试图测试它.有一点我无法理解.

虽然循环是无限的,但如果取消注释threadMessage行,一切正常.为什么?

Jea*_*ard 15

简短的回答

你忘了声明guard为volatile布尔值.


如果你省略了字段的声明volatile,那么你并没有告诉JVM多个线程可以看到这个字段,在你的例子中就是这种情况.

在这种情况下,值guard只读一次会导致无限循环.它将被优化为这样的东西(没有打印):

if(!guard)
{
    while(true)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

现在为什么System.out.println改变这种行为?因为writes是同步的,所以强制线程不缓存读取.

这里使用的println方法之一的代码粘贴:PrintStreamSystem.out.println

public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}
Run Code Online (Sandbox Code Playgroud)

和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;
    }
}
Run Code Online (Sandbox Code Playgroud)

注意同步.