首页 > 解决方案 > 通过静态声明启动时,Java lambda 陷入死锁

问题描述

下面的代码让我很困惑。

import java.util.function.Predicate;

public class Test {

    private final Predicate<String> filter = s -> s != null;

    private boolean started = false;

    private class Runner implements Runnable {
        @Override
        public void run() {
            synchronized ( Test.this ) {
                started = true;
                Test.this.notifyAll();
                traverse("");
            }
        }
    }

    public Test() {
        System.out.println(filter.test(""));
        Thread thread = new Thread(new Runner());
        thread.setDaemon(true);
        thread.start();
    }

    public synchronized String start() {
        while ( !started ) {
            try {
                wait();
            } catch ( InterruptedException ex ) {}
        }
        return "";
    }

    private synchronized void traverse(String s) {
        filter.test(""); // DOES NOT COMPUTE
        filter.test(s);
        System.out.println("not here");
    }

    private static final String STRING = new Test().start(); // POS1

    public static void main(String[] args) {

        System.out.println(STRING); // POS2

    }

}

它卡在DOES NOT COMPUTE. 但是,如果我删除该行POS1并对其进行更改POS2System.out.println(new Test().start())则可以完美运行。在上面的代码中,filter似乎不评估是否Test通过静态变量启动。

为什么会出现这种情况,请问如何解决?

标签: javamultithreadinglambdastaticdeadlock

解决方案


静态字段初始化是类初始化的一部分。您在静态字段初始化期间等待(在主线程中)并且不让类被标记为已初始化。当其他线程看到类状态为其他线程正在进行初始化时,它将被阻塞,直到初始化完成。并且无法通知主线程。这会导致死锁。

至于如何修复它,只需像您在问题中所说的那样在 main 方法中调用它。

以下几行来自 JLS

如果 C 的 Class 对象指示其他线程正在对 C 进行初始化,则释放 LC 并阻塞当前线程,直到通知正在进行的初始化已完成,此时重复此步骤。

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.5 https://docs.oracle.com/javase/specs/jls/se8/html/jls -12.html#jls-12.4.2 https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.1.3


推荐阅读