Deadlock Prevention and avoidance in Java
This tutorial will discuss deadlock prevention and Avoidance in Java programming language.
Introduction
Multithreading in Java includes deadlock. We can multitask by running several threads concurrently in the multithreading environment. Deadlock occurs when threads are always waiting. This can happen from time to time.
A stalemate occurs when more than two threads attempt to access an object that another thread has already acquired. Deadlock is the term for the situation because the threads are waiting for the object to be released. There are more than two threads involved in the scenario. The most significant and frequently requested question in a Java interview is how to prevent deadlock.
Definition:
Every object in the thread has a lock. Java offers a synchronization to lock a block of the code or a method in order to obtain a lock. It permits just one Thread at a time to access that method.
However, a thread initially tries to obtain a lock if it wishes to run a synchronized method. The Thread (which acquires the lock) will have to be patient until the earlier Thread doesn’t let go of the lock if it is feasible that another thread has already obtained the lock.
Example
Let's say there are thread1 and thread2, two threads. The locks of objects obj1 and obj2 were obtained by threads 1 and 3, respectively. Assume that thread2 already has a lock on object 2 and that Thread1 is performing procedure 1 and attempting to acquire the lock on obj2. While thread1 has a lock on obj1, thread2 simultaneously attempts to gain a lock on obj2. In this case, neither thread will finish running, and has to be patient for the lock to be let go. The circumstance is referred to as a deadlock.
Detection of Deadlock in Java
The following techniques can be used to spot a deadlock:
First, we inspect and understand the program to see whether there are any nested synchronized blocks, try to release a lock on different objects, or call synchronized methods from various synchronized methods which could result in a deadlock.
Using the IO gateway is an alternate method of recognizing deadlock. It enables us to upload and examine a thread dump.
Deadlock detection can also be done with jConsole or VisualVM. It displays which threads are locking up concerning which item.
Avoidance of Deadlock in Java
Although avoiding deadlock conditions is impossible, we can do so by employing the following strategies:
Avoid Using Locks: We should only use locks on the members for whom they are necessary. Deadlock results from unnecessary lock usage. The use of a lock-free data structure is advised. Keep our code as clear of locks as we can. For instance, use ConcurrentLinkedQueue rather than synchronized ArrayList.
Avoid Nested Locks: Giving locks to many threads when one Thread already has a lock is another technique to prevent deadlock since we must prevent giving a lock to more than one Thread.
A deadlock can result when two threads wait endlessly for each other to finish using the Thread.join() method. It's always suggestible to utilize the enter with the longest wait time possible if our Thread needs to wait for an alternate or waited for a thread to finish.
Ordering for Use Locks: Each lock should always be given a numerical value. Purchase the locks with the lower number value first, then the lock with the higher numeric value.
Lock Time-out: We can assume the limit of a thread and how long takes to obtain a lock. A thread must wait a predetermined time before trying to place a lock again if it fails to do so the first time.
Let's understand it through an example.
Example:
class Jtp
{
// Sleep method
static void sleep(long mls)
{
try
{
Thread.sleep(mls);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
class SharedThread
{
// synchronized method
synchronized void test1(SharedThread st2)
{
System.out.println("test -1 starts !!");
Jtp.sleep(1000);
st2.test2();
System.out.println("test-1 – end !!!");
}
// another synchronized method
synchronized void test2()
{
System.out.println("test-2 Starts !!");
Jtp.sleep(1000);
System.out.println("test2-end");
}
}
class Thread1 extends Thread
{
private SharedThread st1;
private SharedThread st2;
public Thread1(SharedThread st1, SharedThread st2)
{
this.st1 = st1;
this.st2 = st2;
}
// run method to start a thread
@Override
public void run()
{
st1.test1(st2);
}
}
class Thread2 extends Thread
{
private SharedThread st1;
private SharedThread st2;
public Thread2(SharedThread st1, SharedThread st2)
{
this.st1 = st1;
this.st2 = st2;
}
@Override
public void run()
{
st2.test1(st1);
}
}
public class DeadlockTest
{
public static void main(String s[])
{
SharedThread st1 = new SharedThread();
SharedThread st2 = new SharedThread();
Thread1 t1 = new Thread1(st1, st2);
t1.start();
Thread2 t2 = new Thread2(st1, st2);
t2.start();
// sleeping main Thread
Jtp.sleep(2000);
}
}
Output:

Conclusion
If we are dealing with several locks, it is advised to always buy the locks with the lower numerical value before buying the ones with the higher numerical value.