Home >> Java
Java -> Java Thread Deadlock
How Deadlock may occur while using Java Thread API? a simple to understand
example explained.
Why not check out following link for more information :
http://download.oracle.com/javase/tutorial/essential/concurrency/deadlock.html
Following blog is just trying to depict my understanding for this very
topic, my understanding may be wrong, please correct me by replying/commenting
using comment section on this page.
|
|  |
|
We are going to discuss two scenarios, one, where multi threading example
runs perfectly, and the second one, where Deadlock occurs after running code
for sometime.
In this example, we have two threads, accessing two Java POJO beans,
each bean is accessing the other bean to print the required message,
And methods in both Java beans are made as synchronized.
In this example we shall see that, just by making methods from two beans
accessing one another's synchronized methods, is not going to produce
deadlock, but when these two beans are defined as singleton or multiple
threads are accessing same instances or object references of beans with
multiple synchronized methods accessing one another, then only deadlock
may happen. The reason is that synchronization locks an instance
of a Java Object, not on all occurrence of the same Java Objects if
instantiated multiple times (in case of non singleton approach).
If WorkA and WorkB are defined as normal class (not singleton), then
two threads "Thread 1" and "Thread 2" accessing WorkA and WorkB Java Object
respectively and for Thread1 both WorkA and WorkB objects references are
different from those accessed by Thread2, so here in this case no deadlock
occurs, as each thread completes its task by using two separate instances of
WorkA and WorkB.
But please have a look at the following image:
Diagram: 1
Here both threads 1 and 2 executes by obtaining lock or monitor of
one Java Object (WorkA and WorkB respectively), then deadlock can occur, at
the execution of point number 3 and 4.
In this second scenario WorkA and WorkB are defined as Singleton (one instance
per JVM), Two threads work on only two instance, one of WorkA and another that
of WorkB.
Even if WorkA and WorkB are not Singleton, then also if same instance of WorkA
and WorkB are shared by both the Threads as shown in
Diagram 2, then also
Thread Deadlock may happen, as depicted in diagram below:
Diagram: 2
So comming back to code, following are the required classes as discussed above:
Scenario 1: where both WorkA and WorkB are singleton and two threads Thread1 and
Thread2 are accessing objects of these two Singleton classes:
WorkA.java
package demo;
public class WorkA {
private WorkB workB;
private final static WorkA workA = new WorkA();
private WorkA(){
}
public static WorkA getInstance() {
return workA;
}
public void setWorkB(WorkB workB) {
this.workB = workB;
}
public synchronized void method1() {
workB.method2();
}
public synchronized void method4() {
System.out.println("Inside WorkA.method4....");
}
}
WorkB.java
package demo;
public class WorkB {
private WorkA workA;
private final static WorkB workB = new WorkB();
private WorkB() {
}
public static WorkB getInstance() {
return workB;
}
public void setWorkA(WorkA workA) {
this.workA = workA;
}
public synchronized void method2() {
System.out.println("Inside WorkB.method2....");
}
public synchronized void method3() {
workA.method4();
}
}
TestClient.java - Thread1 and Thread2 as Runnable
package demo;
public class TestClient {
public TestClient() {
WorkA workA = WorkA.getInstance();
WorkB workB = WorkB.getInstance();
workA.setWorkB(workB);
workB.setWorkA(workA);
Thread thread1 = new Thread(new Thread1(workA,workB));
Thread thread2 = new Thread(new Thread2(workA,workB));
thread1.start();
thread2.start();
}
/**
* @param args
*/
public static void main(String[] args) {
new TestClient();
}
}
class Thread1 implements Runnable {
private WorkA workA;
private WorkB workB;
public Thread1(WorkA workA, WorkB workB) {
this.workA = workA;
this.workB = workB;
}
public void run() {
for(int i=0;i<100;i++) {
System.out.println("Calling WorkA.method1()..." +i);
this.workA.method1();
}
}
}
class Thread2 implements Runnable {
private WorkA workA;
private WorkB workB;
public Thread2(WorkA workA, WorkB workB) {
this.workA = workA;
this.workB = workB;
}
public void run() {
for(int i=0;i<100;i++) {
System.out.println("Calling WorkB.method3()..." +i);
this.workB.method3();
}
}
}
|
When I ran this program , following is the output I observed:
Calling WorkA.method1()...0
Inside WorkB.method2....
Calling WorkA.method1()...1
Inside WorkB.method2....
Calling WorkA.method1()...2
Inside WorkB.method2....
Calling WorkA.method1()...3
Inside WorkB.method2....
Calling WorkA.method1()...4
Inside WorkB.method2....
Calling WorkA.method1()...5
Inside WorkB.method2....
Calling WorkA.method1()...6
Inside WorkB.method2....
Calling WorkA.method1()...7
Inside WorkB.method2....
Calling WorkA.method1()...8
Inside WorkB.method2....
Calling WorkA.method1()...9
Calling WorkB.method3()...0
Inside WorkB.method2....
Calling WorkA.method1()...10
|
This means , the moment Thread2 calls WorkB.method3(), it acquires the lock for
WorkB and tries to acquire lock/monitor for WorkA, but WorkA is already getting
used by Thread1. So both Thread1 and Thread2 qaits indefinitely, causing a
Thread Deadlock.
Similarly for Scenario 2: When both WorkA and WorkB are not Singleton,
but both the Threads are using/sharing same instances of WorkA and WorkB
some how. It will be more clear as you go through the following code as well:
WorkA.java
package demo;
public class WorkA {
private WorkB workB;
public void setWorkB(WorkB workB) {
this.workB = workB;
}
public synchronized void method1() {
workB.method2();
}
public synchronized void method4() {
System.out.println("Inside WorkA.method4....");
}
}
WorkB.java
package demo;
public class WorkB {
private WorkA workA;
public void setWorkA(WorkA workA) {
this.workA = workA;
}
public synchronized void method2() {
System.out.println("Inside WorkB.method2....");
}
public synchronized void method3() {
workA.method4();
}
}
TestClient.java
package demo;
public class TestClient {
public TestClient() {
WorkA workA = new WorkA();
WorkB workB = new WorkB();
workA.setWorkB(workB);
workB.setWorkA(workA);
Thread thread1 = new Thread(new Thread1(workA,workB));
Thread thread2 = new Thread(new Thread2(workA,workB));
thread1.start();
thread2.start();
}
/**
* @param args
*/
public static void main(String[] args) {
new TestClient();
}
}
class Thread1 implements Runnable {
private WorkA workA;
private WorkB workB;
public Thread1(WorkA workA, WorkB workB) {
this.workA = workA;
this.workB = workB;
}
public void run() {
for(int i=0;i<100;i++) {
System.out.println("Calling WorkA.method1()..." +i);
this.workA.method1();
}
}
}
class Thread2 implements Runnable {
private WorkA workA;
private WorkB workB;
public Thread2(WorkA workA, WorkB workB) {
this.workA = workA;
this.workB = workB;
}
public void run() {
for(int i=0;i<100;i++) {
System.out.println("Calling WorkB.method3()..." +i);
this.workB.method3();
}
}
}
|
This scenario is showing following output as shown above to me.
Even if we have synchronized methods in Bean in this example,
I can avoid Deadlock in case of Threads accessing these objects just
by creating separate instance of beans for each Thread, so that all
Threads can work separately.
If anyone has some inputs to share, please write to me by using following
comment form, and after review, if found okay, I shall publish your
comment on this, in this Page.
All source code are provided "AS IS" and for reading purpose only.
If anything missed out , please let me know at
techienjoy at yahoo . com