I would like to explain threading deadlocks to newbies. I have seen many examples for deadlocks in the past, some using code and some using illustrations (like the famous 4
Maybe a simple bank situation.
class Account {
double balance;
void withdraw(double amount){
balance -= amount;
}
void deposit(double amount){
balance += amount;
}
void transfer(Account from, Account to, double amount){
sync(from);
sync(to);
from.withdraw(amount);
to.deposit(amount);
release(to);
release(from);
}
}
Obviously, should there be two threads which attempt to run transfer(a, b) and transfer(b, a) at the same time, then a deadlock is going to occur because they try to acquire the resources in reverse order.
This code is also great for looking at solutions to the deadlock as well. Hope this helps!
Here is my detailed example for deadlock, after spending lots of time. Hope it helps :)
package deadlock;
public class DeadlockApp {
String s1 = "hello";
String s2 = "world";
Thread th1 = new Thread() {
public void run() {
System.out.println("Thread th1 has started");
synchronized (s1) { //A lock is created internally (holds access of s1), lock will be released or unlocked for s1, only when it exits the block Line #23
System.out.println("Executing first synchronized block of th1!");
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
System.out.println("Exception is caught in th1");
}
System.out.println("Waiting for the lock to be released from parrallel thread th1");
synchronized (s2) { //As another has runned parallely Line #32, lock has been created for s2
System.out.println(s1 + s2);
}
}
System.out.println("Thread th1 has executed");
}
};
Thread th2 = new Thread() {
public void run() {
System.out.println("Thread th2 has started");
synchronized (s2) { //A lock is created internally (holds access of s2), lock will be released or unlocked for s2, only when it exits the block Line #44
System.out.println("Executing first synchronized block of th2!");
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
System.out.println("Exception is caught in th2");
}
System.out.println("Waiting for the lock to be released from parrallel thread th2");
synchronized (s1) { //As another has runned parallely Line #11, lock has been created for s1
System.out.println(s1 + s2);
}
}
System.out.println("Thread th2 has executed");
}
};
public static void main(String[] args) {
DeadlockApp deadLock = new DeadlockApp();
deadLock.th1.start();
deadLock.th2.start();
//Line #51 and #52 runs parallely on executing the program, a lock is created inside synchronized method
//A lock is nothing but, something like a blocker or wall, which holds access of the variable from being used by others.
//Locked object is accessible, only when it is unlocked (i.e exiting the synchronized block)
//Lock cannot be created for primitive types (ex: int, float, double)
//Dont forget to add thread.sleep(time) because if not added, then object access will not be at same time for both threads to create Deadlock (not actual runtime with lots of threads)
//This is a simple program, so we added sleep90 to create Deadlock, it will execute successfully, if it is removed.
}
//Happy coding -- Parthasarathy S
}
Please see my answer to this question. Bottom line whenever two threads need to acquire two different resources, and do so in different orders then you can get deadlocks.
Simple example from https://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html
public class Deadlock {
public static void printMessage(String message) {
System.out.println(String.format("%s %s ", Thread.currentThread().getName(), message));
}
private static class Friend {
private String name;
public Friend(String name) {
this.name = name;
}
public void bow(Friend friend) {
printMessage("Acquiring lock on " + this.name);
synchronized(this) {
printMessage("Acquired lock on " + this.name);
printMessage(name + " bows " + friend.name);
friend.bowBack(this);
}
}
public void bowBack(Friend friend) {
printMessage("Acquiring lock on " + this.name);
synchronized (this) {
printMessage("Acquired lock on " + this.name);
printMessage(friend.name + " bows back");
}
}
}
public static void main(String[] args) throws InterruptedException {
Friend one = new Friend("one");
Friend two = new Friend("two");
new Thread(new Runnable() {
@Override
public void run() {
one.bow(two);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
two.bow(one);
}
}).start();
}
}
Output:
Thread-0 Acquiring lock on one
Thread-1 Acquiring lock on two
Thread-0 Acquired lock on one
Thread-1 Acquired lock on two
Thread-1 two bows one
Thread-0 one bows two
Thread-1 Acquiring lock on one
Thread-0 Acquiring lock on two
Thread Dump:
2016-03-14 12:20:09
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.74-b02 mixed mode):
"DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x00007f472400a000 nid=0x3783 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-1" #12 prio=5 os_prio=0 tid=0x00007f472420d800 nid=0x37a3 waiting for monitor entry [0x00007f46e89a5000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.anantha.algorithms.ThreadJoin$Friend.bowBack(ThreadJoin.java:102)
- waiting to lock <0x000000076d0583a0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$Friend.bow(ThreadJoin.java:92)
- locked <0x000000076d0583e0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$2.run(ThreadJoin.java:141)
at java.lang.Thread.run(Thread.java:745)
"Thread-0" #11 prio=5 os_prio=0 tid=0x00007f472420b800 nid=0x37a2 waiting for monitor entry [0x00007f46e8aa6000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.anantha.algorithms.ThreadJoin$Friend.bowBack(ThreadJoin.java:102)
- waiting to lock <0x000000076d0583e0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$Friend.bow(ThreadJoin.java:92)
- locked <0x000000076d0583a0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$1.run(ThreadJoin.java:134)
at java.lang.Thread.run(Thread.java:745)
"Monitor Ctrl-Break" #10 daemon prio=5 os_prio=0 tid=0x00007f4724211000 nid=0x37a1 runnable [0x00007f46e8def000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:170)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000076d20afb8> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x000000076d20afb8> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMain$1.run(AppMain.java:93)
at java.lang.Thread.run(Thread.java:745)
"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x00007f47240c9800 nid=0x3794 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #8 daemon prio=9 os_prio=0 tid=0x00007f47240c6800 nid=0x3793 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007f47240c4000 nid=0x3792 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f47240c2800 nid=0x3791 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f47240bf800 nid=0x3790 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f47240be000 nid=0x378f waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f472408c000 nid=0x378e in Object.wait() [0x00007f46e98c5000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076cf88ee0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x000000076cf88ee0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f4724087800 nid=0x378d in Object.wait() [0x00007f46e99c6000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076cf86b50> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076cf86b50> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=0 tid=0x00007f4724080000 nid=0x378c runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f472401f000 nid=0x3784 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f4724021000 nid=0x3785 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f4724022800 nid=0x3786 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f4724024800 nid=0x3787 runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00007f4724026000 nid=0x3788 runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00007f4724028000 nid=0x3789 runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00007f4724029800 nid=0x378a runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00007f472402b800 nid=0x378b runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007f47240cc800 nid=0x3795 waiting on condition
JNI global references: 16
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007f46dc003f08 (object 0x000000076d0583a0, a com.anantha.algorithms.ThreadJoin$Friend),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007f46dc006008 (object 0x000000076d0583e0, a com.anantha.algorithms.ThreadJoin$Friend),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.anantha.algorithms.ThreadJoin$Friend.bowBack(ThreadJoin.java:102)
- waiting to lock <0x000000076d0583a0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$Friend.bow(ThreadJoin.java:92)
- locked <0x000000076d0583e0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$2.run(ThreadJoin.java:141)
at java.lang.Thread.run(Thread.java:745)
"Thread-0":
at com.anantha.algorithms.ThreadJoin$Friend.bowBack(ThreadJoin.java:102)
- waiting to lock <0x000000076d0583e0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$Friend.bow(ThreadJoin.java:92)
- locked <0x000000076d0583a0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$1.run(ThreadJoin.java:134)
at java.lang.Thread.run(Thread.java:745)
Found 1 deadlock.
Heap
PSYoungGen total 74752K, used 9032K [0x000000076cf80000, 0x0000000772280000, 0x00000007c0000000)
eden space 64512K, 14% used [0x000000076cf80000,0x000000076d8520e8,0x0000000770e80000)
from space 10240K, 0% used [0x0000000771880000,0x0000000771880000,0x0000000772280000)
to space 10240K, 0% used [0x0000000770e80000,0x0000000770e80000,0x0000000771880000)
ParOldGen total 171008K, used 0K [0x00000006c6e00000, 0x00000006d1500000, 0x000000076cf80000)
object space 171008K, 0% used [0x00000006c6e00000,0x00000006c6e00000,0x00000006d1500000)
Metaspace used 3183K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 352K, capacity 388K, committed 512K, reserved 1048576K
package test.concurrent;
public class DeadLockTest {
private static long sleepMillis;
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public static void main(String[] args) {
sleepMillis = Long.parseLong(args[0]);
DeadLockTest test = new DeadLockTest();
test.doTest();
}
private void doTest() {
Thread t1 = new Thread(new Runnable() {
public void run() {
lock12();
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
lock21();
}
});
t1.start();
t2.start();
}
private void lock12() {
synchronized (lock1) {
sleep();
synchronized (lock2) {
sleep();
}
}
}
private void lock21() {
synchronized (lock2) {
sleep();
synchronized (lock1) {
sleep();
}
}
}
private void sleep() {
try {
Thread.sleep(sleepMillis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
To run the deadlock test with sleep time 1 millisecond:
java -cp . test.concurrent.DeadLockTest 1
Here's a simple deadlock in C#.
void UpdateLabel(string text) {
lock(this) {
if(MyLabel.InvokeNeeded) {
IAsyncResult res = MyLable.BeginInvoke(delegate() {
MyLable.Text = text;
});
MyLabel.EndInvoke(res);
} else {
MyLable.Text = text;
}
}
}
If, one day, you call this from the GUI thread, and another thread calls it as well - you might deadlock. The other thread gets to EndInvoke, waits for the GUI thread to execute the delegate while holding the lock. The GUI thread blocks on the same lock waiting for the other thread to release it - which it will not because the GUI thread will never be available to execute the delegate the other thread is waiting for. (ofcourse the lock here isn't strictly needed - nor is perhaps the EndInvoke, but in a slightly more complex scenario, a lock might be acquired by the caller for other reasons, resulting in the same deadlock.)