死锁和同步方法

我在Stack Overflow上找到了一个代码,我认为它与我面临的非常相似,但我仍然不明白为什么会出现这种情况。 该示例取自Java中的死锁检测 :

Class A { synchronized void methodA(B b) { b.last(); } synchronized void last() { System.out.println(“ Inside A.last()”); } } Class B { synchronized void methodB(A a) { a.last(); } synchronized void last() { System.out.println(“ Inside B.last()”); } } Class Deadlock implements Runnable { A a = new A(); B b = new B(); // Constructor Deadlock() { Thread t = new Thread(this); t.start(); a.methodA(b); } public void run() { b.methodB(a); } public static void main(String args[] ) { new Deadlock(); } } 

在这种情况下,当调用Deadlock()构造函数时,它将自己作为一个线程启动。 执行此操作时,将调用run()方法。 它将调用b.methodB(a),然后调用a.last()来打印出一个语句。 同时,a.methodA(b)将调用b.last()。 任何对象都没有交叉依赖关系,并且它们也没有同时执行方法。 即使它们是,synchronized参数关键字也会排队,不是吗? 但是,为什么偶尔也会陷入僵局呢? 它不是所有的时间,但它有时会陷入僵局,这是非常不可预测的。 是什么导致这陷入僵局和变通办法?

这两个语句的执行可能是交织的:

 Thread 1: a.methodA(b); //inside the constructor Thread 2: b.methodB(a); //inside run() 

要执行a.methodA() ,线程1将需要获取A对象的锁定。

要执行b.methodB() ,线程2将需要获取B对象的锁。

对于线程1的methodA()然后能够在b实例上调用sychronized方法,它将需要获取由线程2保持的b上的锁定,这将导致线程1等待直到释放该锁定。

对于Thread2的methodB()能够在a实例上调用synchronized方法,它将需要获取由线程1保持的锁 – 这将导致线程2也等待。

由于每个线程都持有另一个线程想要的锁,因此会发生死锁,其中任何一个线程都无法获得它想要的锁,并且两个线程都不会释放它所持有的锁。

重要的是要理解这个代码在运行它时不会产生100%的死锁 – 只有当四个关键步骤(Thread1持有A的锁并试图获得B,线程2持有B的锁并试图获得A)时按特定顺序执行。 运行此代码足够多次,但订单必然会发生。

synchronized会在方法或代码块可以执行之前对必须获取的对象进行锁定。 因为它锁定了整个对象,所以它有时候看起来非常容易使用,但会产生这样的死锁,而不会读取或写入实际有争议的数据。

a.method(b)锁定a对象。 b.method(a)锁定b对象。 并且执行线程都不能继续调用b.last()a.last() ,因为它们都在等待另一个对象释放其锁定。

调用方法A(有效地)锁定(a),锁定(b)。 如果任务然后切换并尝试methodB,则它会立即锁定(b)。