我是多线程的新手,我遇到了这个例子:
public class TestThread {
public static Object Lock1 = new Object();
public static Object Lock2 = new Object();
public static void main(String args[]) {
ThreadDemo1 T1 = new ThreadDemo1();
ThreadDemo2 T2 = new ThreadDemo2();
T1.start();
T2.start();
}
private static class ThreadDemo1 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (Lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}
private static class ThreadDemo2 extends Thread {
public void run() {
synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 2...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 1...");
synchronized (Lock1) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
}
这会导致以下示例输出:
Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...
即存在死锁。但是,如果我们改变在第二个线程中获得的锁的顺序,使它现在看起来像这样:
public class TestThread {
public static Object Lock1 = new Object();
public static Object Lock2 = new Object();
public static void main(String args[]) {
ThreadDemo1 T1 = new ThreadDemo1();
ThreadDemo2 T2 = new ThreadDemo2();
T1.start();
T2.start();
}
private static class ThreadDemo1 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (Lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}
private static class ThreadDemo2 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 2: Holding lock 1...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 2...");
synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
}
它按预期工作,示例输出如下所示:
Thread 1: Holding lock 1...
Thread 1: Waiting for lock 2...
Thread 1: Holding lock 1 & 2...
Thread 2: Holding lock 1...
Thread 2: Waiting for lock 2...
Thread 2: Holding lock 1 & 2...
有人可以向我解释第一个导致死锁的情况,以及为什么第二个代码的更改会修复它?
答案 0 :(得分:5)
这是第一种情况的可能情景:
线程1获取Lock1
并进入休眠状态10毫秒。现在线程2获取Lock2
并进入睡眠状态10毫秒。
现在,线程1尝试获取Lock2
,但是因为它被线程2获取而无法获取它,而线程2尝试获取被线程锁定的Lock1
1。
在第二种情况下,让我们假设第一个线程被选中运行。它得到Lock1
,而另一个帖子被阻止,因为它也试图获得Lock1
。现在,线程1进入休眠状态,然后进入第二个(嵌套的)同步块。它试图得到它(因为它仍然是免费的) - 现在他得到了 2 锁。另一个线程仍然被阻止。
只有在完成执行后,它才会释放两个锁(因为它退出synchronized块),现在JVM可以自由决定选择哪个线程,但它并不是真的重要的是,同样的逻辑发生了。
答案 1 :(得分:2)
您在此处看到的是锁定顺序,这是防止死锁的常用方法。
在第一种情况下,请考虑以下执行顺序,其中当前指令指针位于每个线程的标记位置:
Thread 1:
obtain lock 1
===>
obtain lock 2
Thread 2:
obtain lock 2
===>
obtain lock 1
现在,线程1尝试接下来获取锁定2,但是不能因为线程2保持锁定2.并且线程2尝试接下来获得锁定1,但是不能,因为它由线程1保持。这是经典的循环资源依赖,从而导致死锁。
防止出现这种情况的全局方法是确保所有锁具有总顺序,并且始终以该总顺序获取锁。
证明这项工作是微不足道的:因为所有锁定依赖关系在整个订单中都是“向下”,所以你不能有锁定周期。
答案 2 :(得分:1)
在你的第一段代码中,第一个主题是Lock1
,第二个主要是Lock2
。然后他们试图抓住彼此的锁,但是它们失败了,因为其他锁已经被另一个线程保持,因此会产生死锁。
然而,在第二段代码中,第二个线程没有保持Lock2
并被阻止,直到第一个线程释放第一个锁。第一个线程将在它抓住第二个线程之后释放第一个锁定,如输出所示。