我的一位朋友向我展示了他的代码,我认为这两个线程可能会陷入僵局,因为他们在尝试获取不同变量的锁时可能会死锁:sb1
和sb2
。
当我运行代码时,它们似乎没有死锁,因为我能够看到输出:
A
B
second thread: AB
second thread: BA
以下代码:
public static void main(String[] args) {
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
new Thread() {
public void run() {
synchronized (sb1) {
sb1.append("A");
synchronized (sb2) {
sb2.append("B");
System.out.println(sb1.toString());
System.out.println(sb2.toString());
}
}
}
}.start();
new Thread() {
public void run() {
synchronized (sb2) {
sb2.append("A");
synchronized (sb1) {
sb1.append("B");
System.out.println("second thread: " + sb1.toString());
System.out.println("second thread: " + sb2.toString());
}
}
}
}.start();
}
那么这两个线程可能会陷入僵局吗?
答案 0 :(得分:4)
您发布的代码有潜在的死锁。如果它成功运行,那就意味着你很幸运。
为了演示潜在的死锁,您可以确定时间以确保发生死锁。
public static void main(String[] args) {
final StringBuilder sb1 = new StringBuilder();
final StringBuilder sb2 = new StringBuilder();
new Thread() {
public void run() {
synchronized (sb1) {
sb1.append("A");
System.out.println("Thread 1 has sync sb1");
try { Thread.sleep(700); }
catch (InterruptedException e) { e.printStackTrace(); return; }
System.out.println("Waiting for thread 1 to sync sb2");
synchronized (sb2) {
sb2.append("B");
System.out.println(sb1.toString());
System.out.println(sb2.toString());
}
}
}
}.start();
new Thread() {
public void run() {
try { Thread.sleep(500); }
catch (InterruptedException e) { e.printStackTrace(); return; }
synchronized (sb2) {
System.out.println("Thread 2 has sync sb2");
sb2.append("A");
System.out.println("Waiting for thread 2 to sync sb1");
synchronized (sb1) {
sb1.append("B");
System.out.println("second thread: " + sb1.toString());
System.out.println("second thread: " + sb2.toString());
}
}
}
}.start();
}
现在第一个线程肯定会在sb1
上获得同步,第二个线程将在sb2
上获得同步,然后您将陷入僵局。
输出:
Thread 1 has sync sb1
Thread 2 has sync sb2
Waiting for thread 2 to sync sb1
Waiting for thread 1 to sync sb2
答案 1 :(得分:3)
您没有遇到死锁的事实并不意味着不会发生死锁。
当两个线程试图以相反的顺序在两个不同的资源上获取监视器时,你正确地推断出死锁可能。
因此,此代码可能会产生死锁。
但是,如果两个线程中的任何一个设法在另一个之前获取两个监视器,则不会发生死锁(这似乎与您的执行一起发生)。
以下是如何发生死锁:
sb1
sb2
sb2
上的锁定,该锁定由第二个线程sb1
上的锁定,该锁定由第一个线程由于没有线程会释放它的锁并且两个都在等待,所以你会遇到死锁。
注意:正如Khelwood建议的那样,强制线程进入休眠状态可能会阻止其中一个线程首先获取这两个锁,从而产生死锁。
答案 2 :(得分:1)
该代码解释了一个简单的死锁。
最容易辨别的方法基本上是因为你的Threads彼此之间存在循环依赖关系。
此代码很多次都会导致死锁。
答案 3 :(得分:0)
是的,您的程序可能会陷入僵局。这是一个基于@khelwood答案的更动态的例子。它为实际的字符串附加添加了一些延迟,并在while循环中重复它。所以你可以看到正在发生的事情,迟早会导致程序陷入僵局。要识别死锁情况,请使用ThreadMXBean.findDeadlockedThreads()。
package stack43323164;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.List;
import java.lang.management.ThreadInfo;
public class HowToDemonstrateDeadlock {
private static List<ThreadInfo> findDeadlocks() {
ThreadMXBean tmxb = ManagementFactory.getThreadMXBean();
long[] result = tmxb.findDeadlockedThreads();
if (result == null)
return java.util.Collections.emptyList();
return java.util.Arrays.asList(tmxb.getThreadInfo(result, 2));
}
public static void main(String[] args) {
final StringBuilder sb1 = new StringBuilder();
final StringBuilder sb2 = new StringBuilder();
long monitorDelay=1000L;
//You can play with the delay times to modify the results
long threadOneDelay=100L;
long threadTwoDelay=100L;
new Thread() {
public void run() {
try {
while (true) {
synchronized (sb1) {
sb1.append("A");
System.out.println("Thread 1 has sync sb1");
System.out.println("Waiting for thread 1 to sync sb2");
synchronized (sb2) {
sb2.append("B");
System.out.println(sb1.toString());
System.out.println(sb2.toString());
}
}
Thread.sleep(threadOneDelay);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
new Thread() {
public void run() {
try {
while (true) {
synchronized (sb2) {
System.out.println("Thread 2 has sync sb2");
sb2.append("A");
System.out.println("Waiting for thread 2 to sync sb1");
synchronized (sb1) {
sb1.append("B");
System.out.println("second thread: " + sb1.toString());
System.out.println("second thread: " + sb2.toString());
}
}
Thread.sleep(threadTwoDelay);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
new Thread() {
public void run() {
try {
while (true) {
List<ThreadInfo> deadlocks = findDeadlocks();
if (!deadlocks.isEmpty()) {
for (ThreadInfo i : deadlocks) {
System.out.println("Deadlock detected on thread " + i.getThreadId() + "\n" + i);
}
//Not a chance to solve the situation - boom
System.exit(1);
} else {
System.out.println("No deadlock so far.");
}
Thread.sleep(monitorDelay);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
}