同时执行 Thread.interrupt() Object.notify() 为什么会有两个结果?

execute Thread.interrupt() Object.notify() at the same time, why does has two results?

public class WaitNotifyAll {
    private static volatile Object resourceA = new Object();

    public static void main(String[] args) throws Exception {
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resourceA) {
                    try {
                        System.out.println("threadA begin wait");
                        resourceA.wait();
                        System.out.println("threadA end wait");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread threaB = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resourceA) {
                    System.out.println("threadC begin notify");
                    threadA.interrupt();
                    resourceA.notify();
                }
            }
        });

        threadA.start();

        Thread.sleep(1000);

        threaB.start();

        System.out.println("main over");
    }
 }

这里有两种可能的结果:

  1. 抛出 InterruptedException

  2. 正常终止

为什么?

我不明白。当 threadA 被中断时,结果应该抛出 InterruptedException。但是有时候执行这个程序,可以正常结束。

环境: java8, mac

因为重新排序。在正常终止编译器重新排序指令中断和通知,中断在工作线程上调用并且没有中断异常抛出。 尝试通过读取 volatile 变量来禁止重新排序,你总是会遇到中断异常。

public class WaitNotifyAll {
private static volatile Object resourceA = new Object();

public static void main(String[] args) throws Exception {
    Thread threadA = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (resourceA) {
                try {
                    System.out.println("threadA begin wait");
                    resourceA.wait();
                    System.out.println("threadA end wait");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    Thread threaB = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (resourceA) {
                System.out.println("threadC begin notify");
                threadA.interrupt();
                System.out.print(resourceA);
                resourceA.notify();
            }
        }
    });

    threadA.start();

    Thread.sleep(1000);

    threaB.start();

    System.out.println("main over");
}

}

当线程同时收到中断和通知时,行为可能会有所不同。

请参考https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.2.3

致谢 - Alex Otenko 在 Concurrency Interest 邮件列表中