A deadlock occurs when two or more threads are blocked forever because they are waiting for each other to release resources. In other words, each thread holds a resource and waits for a resource held by another thread, creating a circular dependency.
How Deadlocks Happen
Deadlocks typically happen when:
- Multiple threads hold locks on resources.
- Threads acquire locks in different orders.
- A thread holds a lock and waits for another thread to release a lock, while the other thread is waiting for the first one to release a lock.
Deadlock: Deadlock occurs when two or more threads have a circular dependency on a pair of synchronized objects. For example, Thread 1 holds Lock A and waits for Lock B, while Thread 2 holds Lock B and waits for Lock A.
Example Scenario:
- Consider two resources Resource1 and Resource2 and two threads Thread1 and Thread2.
- Thread1 locks Resource1 and then tries to lock Resource2.
- Thread2 locks Resource2 and then tries to lock Resource1.
- If Thread1 locks Resource1 and waits for Resource2, and Thread2 locks Resource2 and waits for Resource1, a deadlock occurs.
- This scenario is demonstrated in following program.
//ThreadDeadLockDemo.java class Resource { private final Object lock = new Object(); public void method1(Resource other) { synchronized (lock) { System.out.println(Thread.currentThread().getName() + ": locked resource 1"); try { Thread.sleep(50); } catch (InterruptedException e) {} other.method2(); } } public void method2() { synchronized (lock) { System.out.println(Thread.currentThread().getName() + ": locked resource 2"); } } } public class ThreadDeadLockDemo { public static void main(String[] args) { Resource resource1 = new Resource(); Resource resource2 = new Resource(); Thread thread1 = new Thread(() -> { resource1.method1(resource2); }, "Thread 1"); Thread thread2 = new Thread(() -> { resource2.method1(resource1); }, "Thread 2"); thread1.start(); thread2.start(); System.out.println("Press Ctrl+c to stop operation"); } } /* C:\>javac ThreadDeadLockDemo.java C:\>java ThreadDeadLockDemo Press Ctrl+c to stop operation Thread 2: locked resource 1 Thread 1: locked resource 1 */
Correct Solution for above program
// ThreadDeadLockSolution.java class Resource { private final Object lock = new Object(); public void method1(Resource other) { Resource first = this; Resource second = other; // Enforce a strict lock order to avoid deadlocks if (System.identityHashCode(first) > System.identityHashCode(second)) { first = other; second = this; } System.out.println(Thread.currentThread().getName() + ": Trying to acquire lock on resource 1"); synchronized (first.lock) { System.out.println(Thread.currentThread().getName() + ": locked resource 1"); try { Thread.sleep(50); } catch (InterruptedException e) {} System.out.println(Thread.currentThread().getName() + ": Trying to acquire lock on resource 2"); synchronized (second.lock) { System.out.println(Thread.currentThread().getName() + ": locked resource 2"); other.method2(); } System.out.println(Thread.currentThread().getName() + ": released lock on resource 2"); } System.out.println(Thread.currentThread().getName() + ": released lock on resource 1"); } public void method2() { synchronized (lock) { System.out.println(Thread.currentThread().getName() + ": locked resource 2 (inside method2)"); System.out.println(Thread.currentThread().getName() + ": released lock on resource 2 (inside method2)"); } } } public class ThreadDeadLockSolution { public static void main(String[] args) { Resource resource1 = new Resource(); Resource resource2 = new Resource(); Thread thread1 = new Thread(() -> { resource1.method1(resource2); }, "Thread 1"); Thread thread2 = new Thread(() -> { resource2.method1(resource1); }, "Thread 2"); System.out.println("Attempting to start threads..."); thread1.start(); thread2.start(); // Simulate Deadlock Detection Message (In real cases, it may need a watchdog timer) try { Thread.sleep(100); System.out.println("Potential deadlock situation detected!"); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Deadlock avoided due to lock ordering."); System.out.println("Press Ctrl+C to stop operation."); } } /* C:\>javac ThreadDeadLockSolution.java C:\>java ThreadDeadLockSolution Attempting to start threads... Thread 1: Trying to acquire lock on resource 1 Thread 2: Trying to acquire lock on resource 1 Thread 1: locked resource 1 Thread 1: Trying to acquire lock on resource 2 Thread 1: locked resource 2 Thread 1: locked resource 2 (inside method2) Thread 1: released lock on resource 2 (inside method2) Thread 1: released lock on resource 2 Thread 1: released lock on resource 1 Thread 2: locked resource 1 Potential deadlock situation detected! Thread 2: Trying to acquire lock on resource 2 Deadlock avoided due to lock ordering. Press Ctrl+C to stop operation. Thread 2: locked resource 2 Thread 2: locked resource 2 (inside method2) Thread 2: released lock on resource 2 (inside method2) Thread 2: released lock on resource 2 Thread 2: released lock on resource 1 */
Deadlocks can severely impact performance and cause threads to be stuck indefinitely. It’s crucial to design thread synchronization carefully, maintain a consistent lock order, and monitor for potential deadlocks to ensure smooth multi-threaded execution.