We all know that there are a lot of things in JAVA and to all of us (or rather to all the people I met till date, feel that Threads are the most tricky to understand as far as their real time behavior is concerned). This topic is about the behavior of the join method and its comparison to the wait method. I am making this comparison because the join method calls the wait method internally.
Wait() : If a thread calls wait method on an object (obliviously that thread must already have acquired the lock of the object it is calling the wait method on, otherwise a runtime exception is thrown), that thread releases the object and goes to the waiting state. So in all the wait call forces the thread to release the object it is calling wait on.
Join() : If a thread (name it T1) calls the join method on another Thread (remember the wait method is in the Object class and the join method is specific to the Thread class and is present in the Thread class) named T2, then T1 waits for T2 to complete its execution before it continues from that point. Now think what will happen. Will thread T1 leave the locks on the object it has acquired lock on or not ? If we consider the fact that the join method internally calls the wait method the answer that pops up immediately is that “obviously it should leave the lock“, which is WRONG !!!!
For a moment lets assume this answer is correct. Now imagine the scenario, where the thread T1 has called this wait inside a synchronized lock where it has acquired a lock on a String object “abc” and has called the T2.join() inside this block. Now as this call has been made by T1 inside the synchronized block, it obviously means that it needs the lock on the “abc” object to continue the execution further. If T1 has released the lock on “abc” object it will be rescheduled by the thread scheduler to acquire lock on “abc”. But it is not guaranteed tha it will acquire the lock as soon as the thread T2 completes. So in this case some other thread T3 might acquire the lock on “abc” and enter the same synchronized block which the thread T1 already half way !!!! So if the join method makes the calling thread leave the locks, then whole whole use of the synchronized block is ruined.
Wait() inside Join() mystery : The wait method call in side the join method implementation is being called on “this” i.e the thread on which join was called (T2). That wait call does not have to do anything with the calling thread (T1). That means the join call does not causes the calling thread to release a lock on any object and it simply makes it wait till the thread it called join on completes its execution keeping all the locks it already acquired.
I tried the above theory with the following example code:
class AThread extends Thread
{
public void run()
{
System.out.println(“T2 started”);
synchronized (Test2.lock)
{
System.out.println(“Hi hello”);
}
}
}
public class Test implements Runnable
{
public static final String lock = “lock”;
public static final String lock1 = “lock1″;
static Test t = new Test();static Thread T1 = new Thread(t, “Thread T1″);
static AThread T2 = new AThread();public static void main(String[] args)
{
T1.start();
Thread.yield();
T2.start();
}
public void run()
{
synchronized (lock)
{
System.out.println(“T1 started”);
try
{
System.out.println(“waiting for T2 to join”);
T2.join();
}
catch (InterruptedException e)
{
System.out.println(“e.getMessage() = ” + e.getMessage());
}
System.out.println(“T1 ended”);
}
}
}
Running the above program produces the following output:
T1 started
T2 started
waiting for T2 to join
And the execution locks here as Thread T2 tries o acquire lock on the “lock” object which has already been acquired by thread T1 and as T1 has called join on T2, T1 does not leave the lock on the “lock” object and thus a deadlock occurs.
Note: The call to the yield() between the cal to the start() method of T1 and T2 is just to increase the possibility of starting the T1 thread and go a lil ahead on the execution line before the ThreadT2 is started. If the thread T2 starts first, then there would br no deadlock here as the lock will be gained by thread T1 and the execution will complete smoothly. Remember the above example is a 99% deadlock situation and so you should always avoid such situations.