You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何重新初始化线程同步所用对象不会引发死锁?

为什么这段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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 05:25:15