How to Create Deadlock in Java

A deadlock can occur in Java multithreaded programs when one thread is waiting on another thread and vice versa. Since each thread is waiting, the program is deadlocked and cannot proceed.

A deadlock can be simulated in a multi-threaded program if there are two resources and two threads. Let us call them R1/R2 and T1/T2. T1 and T2 needs both R1 and R2. T1 acquires R1 and then tries to acquire R2. At the same time T2 acquires R2 and then tries to acquire R1. T1 has R1 but before it can get R2, T2 has acquired R2. Now T2 cannot get R1 since R1 is already with T1! Now we have a deadlock and the program is stuck forever.

The following program demonstrates deadlock in Java threads. We have used two strings for R1 and R2. Also note that we have added sufficient delay between acquisition of resources in a single thread so that deadlocks will happen all the time. If we remove the delay, deadlocks become unpredictable!

// Java example program on thread deadlock
public class DeadLockSample {

    // Simulates global resources shared by multiple threads
    String R1 = "R1";
    String R2 = "R2";

    Thread T1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Starting T1");

            // Acquired R1
            synchronized (R1) {
                try {
                    // Sleep is added to make deadlock predictable
                    // One second delay ensures that the other thread
                    // acquires lock on R2!
                    Thread.sleep(100);
                } catch (Exception ex) {
                }
                synchronized (R2) {
                    System.out.println("Acquired both!");
                }
            }

            // If we reach here, no deadlock!
            System.out.println("Completed T1");
        }
    });

    Thread T2 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Starting T2");
            synchronized (R2) {
                try {
                    // Sleep is added to make deadlock predictable
                    // One second delay ensures that the other thread
                    // acquires lock on R1!
                    Thread.sleep(100);
                } catch (Exception ex) {
                }
                synchronized (R1) {
                    System.out.println("Acquired both!");
                }
            }
            // If we reach here, no deadlock!
            System.out.println("Completed T2");
        }
    });

    // Java example program on thread deadlock
    public static void main(String[] args) {
        DeadLockSample ds = new DeadLockSample();
        ds.T1.start();
        ds.T2.start();
    }
}

Unpredictable deadlocks can be a serious issue in applications running in the production environment. Hence it is very important to take a close look at the code if you are using multithreading in your programs.

Deadlocks in Java threads can be minimized by avoiding nested locks. Also lock only those resources that are used and shared across multiple threads.