0
0
JavaProgramBeginner · 2 min read

Java Program to Implement Deadlock Example

A Java deadlock example uses two threads each locking one resource and then trying to lock the other's resource, causing a standstill; for example, use synchronized blocks on two objects in different order in two threads to create deadlock.
📋

Examples

InputRun the program as is
OutputThread-1: Locked Resource 1 Thread-2: Locked Resource 2 (Thread-1 and Thread-2 wait forever, no further output)
InputSwap the order of locks in one thread
OutputThread-1: Locked Resource 1 Thread-1: Locked Resource 2 Thread-2: Locked Resource 2 Thread-2: Locked Resource 1 (No deadlock, threads complete)
InputUse only one resource lock
OutputThread-1: Locked Resource 1 Thread-2: Locked Resource 1 (Threads wait for each other but no deadlock)
🧠

How to Think About It

To create a deadlock in Java, you need two or more threads and two or more resources. Each thread locks one resource and then tries to lock the other resource that another thread holds. Because each thread waits for the other to release its lock, they get stuck forever, causing a deadlock.
📐

Algorithm

1
Create two resource objects to lock on.
2
Create two threads, each trying to lock the two resources in opposite order.
3
In each thread, lock the first resource using synchronized block.
4
Pause briefly to increase chance of deadlock.
5
Then try to lock the second resource inside the first lock.
6
Run both threads simultaneously to cause deadlock.
💻

Code

java
public class DeadlockExample {
    private static final Object resource1 = new Object();
    private static final Object resource2 = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread-1: Locked Resource 1");
                try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
                synchronized (resource2) {
                    System.out.println("Thread-1: Locked Resource 2");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("Thread-2: Locked Resource 2");
                try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
                synchronized (resource1) {
                    System.out.println("Thread-2: Locked Resource 1");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}
🔍

Dry Run

Let's trace the deadlock example through the code with actual thread actions.

1

Thread-1 locks resource1

Thread-1 enters synchronized block on resource1 and prints "Thread-1: Locked Resource 1".

2

Thread-2 locks resource2

Thread-2 enters synchronized block on resource2 and prints "Thread-2: Locked Resource 2".

3

Thread-1 tries to lock resource2

Thread-1 waits because resource2 is locked by Thread-2.

4

Thread-2 tries to lock resource1

Thread-2 waits because resource1 is locked by Thread-1.

5

Deadlock occurs

Both threads wait forever for each other to release locks, causing deadlock.

ThreadLocked ResourceWaiting For
Thread-1resource1resource2
Thread-2resource2resource1
💡

Why This Works

Step 1: Two resources locked in opposite order

Each thread locks one resource first, then tries to lock the other resource that the other thread holds, creating a circular wait.

Step 2: Synchronized blocks enforce exclusive access

The synchronized keyword ensures only one thread can hold the lock on a resource at a time.

Step 3: Sleep increases chance of deadlock

The Thread.sleep(100) pauses the thread to let the other thread lock its resource, making deadlock more likely.

Step 4: Both threads wait forever

Because each thread waits for the other to release a lock, neither can proceed, causing the program to hang.

🔄

Alternative Approaches

Avoid deadlock by locking resources in same order
java
public class NoDeadlockExample {
    private static final Object resource1 = new Object();
    private static final Object resource2 = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread-1: Locked Resource 1");
                try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
                synchronized (resource2) {
                    System.out.println("Thread-1: Locked Resource 2");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread-2: Locked Resource 1");
                try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
                synchronized (resource2) {
                    System.out.println("Thread-2: Locked Resource 2");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}
Locking resources in the same order prevents circular wait and thus deadlock.
Use tryLock with timeout (java.util.concurrent.locks.Lock)
java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;

public class TryLockDeadlock {
    private static final Lock lock1 = new ReentrantLock();
    private static final Lock lock2 = new ReentrantLock();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            try {
                if(lock1.tryLock(500, TimeUnit.MILLISECONDS)) {
                    System.out.println("Thread-1: Locked lock1");
                    Thread.sleep(100);
                    if(lock2.tryLock(500, TimeUnit.MILLISECONDS)) {
                        System.out.println("Thread-1: Locked lock2");
                        lock2.unlock();
                    }
                    lock1.unlock();
                }
            } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        });

        Thread t2 = new Thread(() -> {
            try {
                if(lock2.tryLock(500, TimeUnit.MILLISECONDS)) {
                    System.out.println("Thread-2: Locked lock2");
                    Thread.sleep(100);
                    if(lock1.tryLock(500, TimeUnit.MILLISECONDS)) {
                        System.out.println("Thread-2: Locked lock1");
                        lock1.unlock();
                    }
                    lock2.unlock();
                }
            } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        });

        t1.start();
        t2.start();
    }
}
Using tryLock with timeout avoids deadlock by giving up locks if not acquired in time.

Complexity: O(1) time, O(1) space

Time Complexity

The deadlock example runs in constant time as it only involves locking two resources without loops.

Space Complexity

Only two resource objects and two threads are used, so space is constant.

Which Approach is Fastest?

The basic synchronized approach is simple but can deadlock; tryLock avoids deadlock but adds overhead.

ApproachTimeSpaceBest For
Synchronized blocks (deadlock)O(1)O(1)Demonstrating deadlock
Synchronized blocks (same order)O(1)O(1)Safe locking without deadlock
tryLock with timeoutO(1)O(1)Avoiding deadlock with retries
💡
Always acquire multiple locks in the same order to prevent deadlocks.
⚠️
Beginners often lock resources in different orders, causing deadlock without realizing it.