主线程无法唤醒?Java静态代码块线程同步问题排查
主线程在静态代码块中wait后无法被唤醒的原因分析
问题重现
调用静态方法时,静态代码块内的主线程进入wait状态后,始终无法被预期的线程唤醒,相关代码如下:
Main类代码
public class Main { public static void main(String[] args) { MyFactory.getSomething(); System.out.println("over"); } }
MyFactory类代码
import java.util.concurrent.TimeUnit; public class MyFactory { private static Object lock = new Object(); static{ init(); } private static void init(){ new Thread(()->{ try { // 让该线程晚于主线程执行 TimeUnit.SECONDS.sleep(3); System.out.println("task run..."); synchronized (lock){ lock.notifyAll(); // 唤醒主线程 } } catch (InterruptedException e) { throw new RuntimeException(e); } },"demo-thread").start(); synchronized (lock){ try { System.out.println("waiting..."); lock.wait(); // 进入等待 System.out.println("wake up"); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public static Object getSomething(){ return null; } }
实际输出
waiting...
预期输出
waiting... task run... wake up over
尝试的解决方法
将静态代码块中直接调用init()的逻辑,改为在新线程中执行init():
- 原代码:
static{ init(); } - 修改后代码:
static{ new Thread(()->init()).start(); }
修改后程序正常运行,输出如下:
over waiting... task run... wake up
问题核心原因
你的猜测完全正确,问题根源是主线程阻塞导致MyFactory类的初始化未完成,具体逻辑链如下:
- 主线程执行
MyFactory.getSomething()时,触发MyFactory类的加载初始化流程,此时主线程会持有MyFactory.class的类初始化锁,直到类初始化(静态代码块执行完毕)完成才会释放。 - 静态代码块调用
init(),先启动demo-thread,随后主线程获取lock锁并进入wait状态(此时会释放lock锁,但不会释放MyFactory.class的初始化锁)。 demo-thread启动后,在执行synchronized (lock)之前需要访问MyFactory的静态变量lock,但此时MyFactory类还处于初始化未完成状态(主线程卡在init()的wait处,静态代码块没执行完),demo-thread会被阻塞在类初始化的等待队列中,根本无法走到获取lock锁并执行notifyAll的步骤。- 最终主线程无法被唤醒,
demo-thread也无法继续执行,程序陷入永久阻塞。
而修改为在新线程中执行init()后,主线程会快速完成MyFactory的类初始化(静态代码块仅启动线程就结束),释放类初始化锁,后续所有线程都能正常访问类成员,因此程序可以正常运行。
内容的提问来源于stack exchange,提问作者Q10Viking
相关产品推荐
相关产品推荐

