为何未发生死锁?先调用a.foo(b)再启动线程无死锁的原因
为什么先调用
a.foo(b)再启动线程不会死锁? 首先咱们先把代码补全,还原经典的死锁场景(你的代码里A类的last()没写完,B类也缺失,我补全成常见的死锁示例):
class A { synchronized void foo(B b) { String name = Thread.currentThread().getName(); System.out.println(name + " entered A.foo"); try { Thread.sleep(2000); } catch (Exception e) { System.out.println("A Interrupted"); } System.out.println(name + " trying to call B.last()"); b.last(); } synchronized void last() { System.out.println("Inside A.last"); } } class B { synchronized void bar(A a) { String name = Thread.currentThread().getName(); System.out.println(name + " entered B.bar"); try { Thread.sleep(2000); } catch (Exception e) { System.out.println("B Interrupted"); } System.out.println(name + " trying to call A.last()"); a.last(); } synchronized void last() { System.out.println("Inside B.last"); } } class Deadlock { public static void main(String[] args) { A a = new A(); B b = new B(); Thread t = new Thread(() -> { b.bar(a); // 线程t要执行的逻辑:拿B的锁,再要A的锁 }); // 重点:先调用a.foo(b),再启动线程t a.foo(b); t.start(); } }
先搞懂死锁的核心:
死锁发生的前提是两个线程各自持有一个对方需要的锁,同时都在等待对方释放锁,四个条件缺一不可:互斥、持有并等待、不可剥夺、循环等待。
现在分析你说的执行流程:
当你先调用a.foo(b),再执行t.start()时,整个过程是这样的:
- 主线程先执行
a.foo(b):- 因为
foo()是synchronized方法,主线程会立刻拿到A对象的锁(同步方法的锁是当前实例,也就是a)。 - 然后主线程进入
sleep(2000),这时候它依然持有A的锁,不会释放。 - 2秒后sleep结束,主线程执行
b.last():last()是B类的同步方法,所以主线程会拿到B对象的锁,执行完B.last()后释放B的锁。 - 最后
a.foo(b)方法执行完毕,主线程释放A的锁。
- 因为
- 主线程才会执行
t.start():- 线程
t启动后,执行b.bar(a):拿到B对象的锁,sleep2秒。 - 2秒后sleep结束,线程
t执行a.last():这时候A的锁已经被主线程释放了,所以线程t能顺利拿到A的锁,执行完A.last()后释放A的锁,最后b.bar(a)执行完毕,释放B的锁。
- 线程
为什么不会死锁?
因为两个线程的锁请求是串行的:主线程先把A锁和B锁的操作全部完成,释放所有锁之后,线程t才开始请求锁。根本不会出现“两个线程各持一个锁、互相等待”的情况,自然就不会触发死锁。
对比一下死锁的场景:
如果把顺序反过来,先t.start()再调用a.foo(b),那才会触发死锁:
- 线程
t启动后拿到B的锁,sleep时主线程调用a.foo(b)拿到A的锁。 - 两者sleep结束后,线程
t要拿A的锁(被主线程拿着),主线程要拿B的锁(被线程t拿着),互相等待,死锁就发生了。
内容的提问来源于stack exchange,提问作者Rajan saha Raju
相关产品推荐
相关产品推荐

