java - 如果在静态初始化 block 中创建线程,程序将挂起

标签 java multithreading deadlock static-initializer

我遇到过程序挂起的情况,看起来像是死锁。但我尝试用 jconsole 和 visualvm 解决这个问题,但他们没有检测到任何死锁。示例代码:

public class StaticInitializer {

private static int state = 10;

static {
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            state = 11;
            System.out.println("Exit Thread");
        }
    });

    t1.start();

    try {
        t1.join();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    System.out.println("exiting static block");
}

public static void main(String...strings) {
    System.out.println(state);
}
}

当我在 Debug模式下执行它时,我可以看到控制到达 @覆盖 公共(public)无效运行(){ 状态 = 11;

但是一旦执行 state=11,它就会挂起/死锁。我查看了 stackoverflow 中的不同帖子,我认为静态初始化程序是线程安全的,但在那种情况下,jconsole 应该报告这一点。关于主线程,jconsole 说它处于等待状态,这很好。但是对于在静态初始化 block 中创建的线程,jconsole 表示它处于 RUNNABLE 状态并且没有被阻塞。我很困惑,这里缺乏一些概念。请帮帮我。

最佳答案

您不只是开始另一个线程 - 您正在加入它。新线程必须等待 StaticInitializer 完全初始化才能继续,因为它正在尝试设置 state 字段...并且初始化已经在进行中,所以它等待。但是,它将永远等待,因为初始化正在等待新线程终止。经典死锁。

参见 Java Language Specification section 12.4.2有关类初始化所涉及内容的详细信息。重要的是,初始化线程将“拥有”StaticInitializer.class 的监视器,但线程将等待获取该监视器。

换句话说,您的代码有点像这个非初始化代码(省略了异常处理)。

final Object foo = new Object();
synchronized (foo)
{
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (foo) {
                System.out.println("In the new thread!");
            }
        });
    t1.start();
    t1.join();
});

如果您能理解为什么代码会死锁,那么您的代码基本上是一样的。

道德是不要在静态初始化器中做太多工作。

关于java - 如果在静态初始化 block 中创建线程,程序将挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43841656/

相关文章:

Java - 在编译时不知道结果类型的情况下,方法如何返回变量类型对象?

c++ - main 在 pthread 之后不继续

UPDATE 上的 SQL Server 死锁

c# - 使用并行复制表会导致 MySQL 死锁

两次锁定相同行的 MySQL 5.6 死锁?

java - 部署 Spring Boot 应用程序时 Heroku 错误代码 H10

java - 在java程序中存储字符串

java - 持久性单元提示数据源已关闭,即使它没有关闭

java - 可重入锁 VS 可重入读写锁

c - 多线程计算矩阵乘积的时间成本