为何重新初始化线程同步所用对象不会引发死锁?
为什么这段Java代码不会引发死锁?
先看示例代码定义:
public class Example { private static Object object; public static synchronized void init() { if (object == null) { object = new Object(); } else { // do nothing } } public static void doSomething() { synchronized (object) { // do something } } public static void reinitialize() { synchronized (object) { try { Thread.sleep(5000l); } catch (Exception e) { } object = new Object(); } } }
测试逻辑:启动100个线程循环调用doSomething,同时启动一个线程调用reinitialize(休眠5秒持有锁),观察线程执行情况:
var threads = IntStream.range(0, 100).mapToObj(i -> new Thread(() -> { Example.init(); for (var count = 0; count < 1000; count++) { Example.doSomething(); } })).toList(); threads.forEach(Thread::start); new Thread(() -> Example.reinitialize()).start(); threads.forEach(t -> { try { t.join(); } catch (Exception e) {} }); // 线程并未死锁,主线程在所有线程完成后结束执行
原本预期reinitialize替换object实例后,阻塞在doSomething的线程会因绑定旧实例永久阻塞,但实际没有死锁,原因如下:
核心逻辑拆解
- 锁的绑定是动态的:每次调用
doSomething时,线程都会先读取当前object变量的引用,再尝试获取该引用指向实例的锁。线程不会提前绑定某个固定的object实例,而是每次执行时都取最新值。 - 旧锁会被正常释放:
reinitialize的synchronized代码块是基于旧object实例加锁,当代码块执行到object = new Object()后,整个代码块执行完毕,旧object的锁会被自动释放。此时所有阻塞在旧锁上的线程会依次获取锁,执行完synchronized代码块后释放旧锁。 - 后续操作切换到新锁:当这些线程再次循环调用
doSomething时,会读取到已经更新后的新object实例,转而尝试获取新实例的锁,不会再和旧锁产生关联。
额外细节补充
init方法是静态同步方法,保证了object只会被初始化一次,所有线程启动后都能拿到有效的初始实例,不会出现object为null的情况。- 死锁的前提是多个线程互相持有对方需要的锁且不释放,但这里不存在这种循环等待的场景:
reinitialize只持有旧object的锁,更新引用后就释放了旧锁;其他线程要么等旧锁,要么后续获取新锁,没有互相阻塞的情况。
内容的提问来源于stack exchange,提问作者BJ Dela Cruz
相关产品推荐
相关产品推荐

