
Data Structure
Networking
RDBMS
Operating System
Java
MS Excel
iOS
HTML
CSS
Android
Python
C Programming
C++
C#
MongoDB
MySQL
Javascript
PHP
- Selected Reading
- UPSC IAS Exams Notes
- Developer's Best Practices
- Questions and Answers
- Effective Resume Writing
- HR Interview Questions
- Computer Glossary
- Who is Who
How can we avoid a deadlock in Java?
This article explains several strategies to avoid deadlock in Java, including a brief introduction to deadlock, strategies, and respective examples.
Deadlock in Java
In case of multi-threading, a deadlock is a programming situation where two or more threads are holding resources needed by other threads and are blocked forever, waiting for each other (to release the required resource).
A deadlock condition will occur with at least two threads and two or more resources.
How To Avoid Deadlock in Java?
Following is a list of strategies to avoid the deadlock in Java:
-
Avoid Nested Locks: A deadlock mainly happens when we give locks to multiple threads. Avoid giving a lock to multiple threads if we have already given it to one.
-
Avoid Unnecessary Locks: We can have a lock only for those members which are required. Having a lock unnecessarily can lead to a deadlock.
-
Using Thread.join(): A deadlock condition appears when one thread is waiting other to finish. If this condition occurs, we can use Thread.join() with the maximum time the execution will take.
Avoid Deadlock by Avoiding the Nested Locks
Sometimes, the deadlocks also occur due to nested locks, where a thread holds one lock and waits for another. By avoiding nested locks and acquiring one lock at a time, we can avoid the deadlock.
Example
In the following program, we avoid deadlock by not acquiring nested locks. Instead, each thread completes its work with one lock, releases it, and then tries to acquire the next lock:
public class DeadlockTest { public static void main(String[] args) throws InterruptedException { Object obj1 = new Object(); Object obj2 = new Object(); Object obj3 = new Object(); Thread t1 = new Thread(new SyncThread(obj1, obj2), "t1"); Thread t2 = new Thread(new SyncThread(obj2, obj3), "t2"); t1.start(); Thread.sleep(2000); t2.start(); Thread.sleep(2000); } } class SyncThread implements Runnable { private Object obj1; private Object obj2; public SyncThread(Object o1, Object o2){ this.obj1=o1; this.obj2=o2; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " acquiring lock on " + obj1); synchronized (obj1) { System.out.println(name + " acquired lock on " + obj1); work(); } System.out.println(name + " released lock on " + obj1); System.out.println(name + " acquiring lock on " + obj2); synchronized (obj2) { System.out.println(name + " acquired lock on " + obj2); work(); } System.out.println(name + " released lock on " + obj2); System.out.println(name + " finished execution."); } private void work() { try { Thread.sleep(5000); } catch (InterruptedException ie) { ie.printStackTrace(); } } }
The above program produces the following output:
t1 acquiring lock on java.lang.Object@27a54c50 t1 acquired lock on java.lang.Object@27a54c50 t2 acquiring lock on java.lang.Object@2ee6a5aa t2 acquired lock on java.lang.Object@2ee6a5aa t1 released lock on java.lang.Object@27a54c50 t1 acquiring lock on java.lang.Object@2ee6a5aa t2 released lock on java.lang.Object@2ee6a5aa t2 acquiring lock on java.lang.Object@20214e06 t1 acquired lock on java.lang.Object@2ee6a5aa t2 acquired lock on java.lang.Object@20214e06 t1 released lock on java.lang.Object@2ee6a5aa t2 released lock on java.lang.Object@20214e06 t1 finished execution. t2 finished execution.
Using Thread.join() Method
In Java, another way to avoid deadlock is by using the Thread.join() method. This method blocks the current thread until the thread on which it is called finishes execution.
Deadlock can occur when threads wait for each other. The join() method helps avoid such deadlock scenarios.
Example
The example given below uses the Thread.join() method to avoid the deadlock:
public class AvoidDeadlockWithJoin { public static void main(String[] args) { Thread t1 = new Thread(() -> { System.out.println("Thread 1 Started...."); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread 1 Finished...."); }); Thread t2 = new Thread(() -> { System.out.println("Thread 2 Started...."); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread 2 Finished...."); }); // Start Thread 1 t1.start(); try { //using join() method t1.join(); // Wait for t1 to finish before starting t2 } catch (InterruptedException e) { e.printStackTrace(); } // Start Thread 2 after t1 is finished t2.start(); try { // Wait for t2 to finish t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Main thread, all threads finished"); } }
Following is the output of the above program:
Thread 1 Started.... Thread 1 Finished.... Thread 2 Started.... Thread 2 Finished.... Main thread , all threads finished