Java:当lock为null时,为什么这是一个死锁?

时间:2017-01-03 10:54:06

标签: java multithreading

我的代码中遇到了死锁情况。我使用以下测试代码重现了该场景(也请查看行号注释)。

我有一个ExecuterService需要3个任务,2个任务在同一个对象上有一个同步块,没有初始化它应该有null值。

java中不允许使用

synchronized(null){},所以我期待line-2出错。但是,这是以死锁执行的。即前三个任务在第三个任务中等待。 (见输出-1)

现在如果我将line-1更改为Object lock = new Object();代码将完美运行而不会出现任何死锁(输出-2)。我只是想知道当我们提供未初始化的互斥对象时JVM到底发生了什么?看起来JVM正在为它创建一个静态副本,它由所有任务共享。因此死锁

public class Test {
    Object lock; //line -1
    Integer a = 10;

    public static void main(String[] arg) {
        new Test().doIt();
    }

    public void doIt() {
        ExecutorService service = Executors.newFixedThreadPool(3);

        service.submit(new Runnable() {
            public void run() {
                synchronized (Test.this.lock) { //line -2
                    try {
                        Thread.sleep(1000 * 5);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    Test.this.a = (int) Thread.currentThread().getId();
                    System.out.println(a + " " + Thread.currentThread().getId());

                }
            }
        });

        service.submit(new Runnable() {
            public void run() {
                synchronized (Test.this.lock) {
                    try {
                        Thread.sleep(1000 * 5);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    Test.this.a = (int) Thread.currentThread().getId();
                    System.out.println(a + " " + Thread.currentThread().getId());

                }
            }
        }

        );

        service.submit(new Runnable() {
            public void run() {
                Test.this.a = (int) Thread.currentThread().getId();
                while (true) {
                    try {
                        Thread.sleep(1 * 1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println(a + " Main");
                }
            }
        });

    }
}

输出-1

11 Main
11 Main
11 Main
11 Main
11 Main
11 Main
11 Main
11 Main
11 Main
11 Main
11 Main
.
.
.

输出-2

11 Main
11 Main
11 Main
11 Main
9 9
9 Main
9 Main
9 Main
9 Main
9 Main
10 10
10 Main
10 Main
.
.
.

2 个答案:

答案 0 :(得分:7)

您没有死锁 - 正如您预期的那样出现错误。第一个和第二个任务将抛出NullPointerException,只留下第三个任务。

如果您在run() / try声明中包围catch的正文,则可以看到此内容。

答案 1 :(得分:3)

异常会在执行程序线程上抛出,除非您捕获或查询它,否则不可见。例如,你可以写:

Future<?> f = service.submit(new Runnable() { ... }

在主要结尾处:

try {
  f.get();
} catch (ExecutionException | InterruptedException e) {
  e.printStackTrace();
}

那会告诉你NPE。