问题
One of my friends showed me his code below, and I thought the two threads could be deadlocked, because they could deadlock while trying to acquire locks on the different variables: sb1
and sb2
.
When I run the code, they don't seem to be deadlocked, as I was able to see the output:
A
B
second thread: AB
second thread: BA
Code below:
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();
}
so could the two threads be deadlocked?
回答1:
The fact that you are not getting a deadlock does not imply a deadlock cannot occur.
You are right in inferring that a deadlock might occur when two threads attempt to acquire monitors on two different resources in opposite orders.
Therefore, this code may produce a deadlock.
However, if any of the two threads manages to acquire both the monitors before the other, no deadlock will occur (which seems to be happening with your execution).
Here's how a deadlock might occur instead:
- Thread one starts and acquires lock on
sb1
- Thread two starts and acquires lock on
sb2
- Thread one waits to acquire lock on
sb2
, which is owned by thread two - Thread two waits to acquire lock on
sb1
, which is owned by thread one
As no thread will release its lock and both wait on the other, you get a deadlock.
Note: as Khelwood advises, forcing the threads to sleep may prevent one of the threads from acquiring both locks first, hence producing the deadlock.
回答2:
The code you posted has a potential deadlock. If it runs successfully, that just means you got lucky.
To demonstrate the potential deadlock, you can rig the timing to ensure that a deadlock occurs.
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();
}
Now the first thread will definitely get the sync on sb1
, and the second will get the sync on sb2
, and then you will get a deadlock.
Output:
Thread 1 has sync sb1
Thread 2 has sync sb2
Waiting for thread 2 to sync sb1
Waiting for thread 1 to sync sb2
回答3:
That code explains exactly a simple deadlock.
The easiest way to tell is basically because your Threads hold circular dependencies between each other.
This code alot of the times will result in a deadlock.
回答4:
Yes, your program can end in a deadlock. Here a more dynamic example based on the answer of @khelwood. It adds some delay to the actual string appending and repeats it in a while loop. So you can see what is happening, sooner or later the program will end in a deadlock. To identify the deadlock situation ThreadMXBean.findDeadlockedThreads() is used.
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();
}
}
来源:https://stackoverflow.com/questions/43323164/java-threads-deadlocked