Deadlock and Starvation Java Demo
Enviroment Setup
Java Playground: https://repl.it/languages/java
Deadlock Demo
Deadlock is about holding and waiting another lock forms a loop.
Demo: https://repl.it/@trsong/Deadlock-Demo#Main.java
public class Main {
public static class SyncObject1 {}
public static class SyncObject2 {}
/**
* Deadlock Demo
* In terminal run: java Demo
* In another terminal run to find java PID: jps -mv
* and then: jstack -l <PID>
*
* Found one Java-level deadlock:
* =============================
* "Thread1":
* waiting to lock monitor 0x00007fc09fe13e00 (object 0x000000070fe1b708, a Demo$SyncObject2),
* which is held by "Thread2"
* "Thread2":
* waiting to lock monitor 0x00007fc09fe13f00 (object 0x000000070fe1aa10, a Demo$SyncObject1),
* which is held by "Thread1"
*/
public static void main(String[] args) {
SyncObject1 object1 = new SyncObject1();
SyncObject2 object2 = new SyncObject2();
Thread t1 = new Thread(new SyncAndRunnable(object1, object2, 1, 2, true));
Thread t2 = new Thread(new SyncAndRunnable(object1, object2, 2, 1, false));
t1.setName("Thread1");
t1.start();
t2.setName("Thread2");
t2.start();
}
public static class SyncAndRunnable implements Runnable {
final SyncObject1 obj1;
final SyncObject2 obj2;
final int a, b;
final boolean flag;
public SyncAndRunnable(SyncObject1 obj1, SyncObject2 obj2, int a, int b, boolean flag) {
this.obj1 = obj1;
this.obj2 = obj2;
this.a = a;
this.b = b;
this.flag = flag;
}
@Override
public void run() {
try {
if (flag) {
synchronized (obj1) {
Thread.sleep(3000);
synchronized (obj2) {
System.out.println(a + b);
}
}
} else {
synchronized (obj2) {
Thread.sleep(3000);
synchronized (obj1) {
System.out.println(a + b);
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Starvation Demo
Starvation is about waiting for underlying dependency that has no chance or low priority to be scheduled.
Demo: https://repl.it/@trsong/Starvation-Demo#Main.java
import java.util.concurrent.*;
public class Demo {
/**
* Starvation Demo
* In terminal run: java Demo
* In another terminal run to find java PID: jps -mv
* and then: jstack -l <PID>
*
* "main" #1 prio=5 os_prio=31 cpu=150.72ms elapsed=65.36s tid=0x00007fed14808800 nid=0x2803 waiting on condition [0x0000700002d2e000]
* java.lang.Thread.State: WAITING (parking)
* at jdk.internal.misc.Unsafe.park(java.base@11.0.8/Native Method)
* - parking to wait for <0x000000070fec5c00> (a java.util.concurrent.FutureTask)
* at java.util.concurrent.locks.LockSupport.park(java.base@11.0.8/LockSupport.java:194)
* at java.util.concurrent.FutureTask.awaitDone(java.base@11.0.8/FutureTask.java:447)
* at java.util.concurrent.FutureTask.get(java.base@11.0.8/FutureTask.java:190)
* at Demo.main(Demo.java:36)
*
* "pool-1-thread-1" #13 prio=5 os_prio=31 cpu=0.60ms elapsed=65.21s tid=0x00007fed14024000 nid=0x5f03 waiting on condition [0x000070000406a000]
* java.lang.Thread.State: WAITING (parking)
* at jdk.internal.misc.Unsafe.park(java.base@11.0.8/Native Method)
* - parking to wait for <0x000000070fd01760> (a java.util.concurrent.FutureTask)
* at java.util.concurrent.locks.LockSupport.park(java.base@11.0.8/LockSupport.java:194)
* at java.util.concurrent.FutureTask.awaitDone(java.base@11.0.8/FutureTask.java:447)
* at java.util.concurrent.FutureTask.get(java.base@11.0.8/FutureTask.java:190)
* at Demo$MyCallable.call(Demo.java:46)
* at Demo$MyCallable.call(Demo.java:41)
* at java.util.concurrent.FutureTask.run(java.base@11.0.8/FutureTask.java:264)
* at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@11.0.8/ThreadPoolExecutor.java:1128)
* at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@11.0.8/ThreadPoolExecutor.java:628)
* at java.lang.Thread.run(java.base@11.0.8/Thread.java:834)
*/
public static void main(String[] args) throws ExecutionException, InterruptedException {
Future<String> submit = single.submit(new MyCallable());
System.out.println(submit.get());
System.out.println("over");
single.shutdown();
}
public static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("In MyCallable");
Future<String> submit = single.submit(new AnotherCallable());
return "success:" + submit.get();
}
}
public static class AnotherCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("In another callable");
return "another callable success";
}
}
private final static ExecutorService single = Executors.newSingleThreadExecutor();
}