Java : Does wait() release lock from synchronized block Java : Does wait() release lock from synchronized block multithreading multithreading

Java : Does wait() release lock from synchronized block


"Invoking wait inside a synchronized method is a simple way to acquire the intrinsic lock"

This sentence is false, it is an error in documentation.

Thread acquires the intrinsic lock when it enters a synchronized method.Thread inside the synchronized method is set as the owner of the lock and is in RUNNABLE state.Any thread that attempts to enter the locked method becomes BLOCKED.

When thread calls wait it releases the current object lock (it keeps all locks from other objects) and than goes to WAITING state.

When some other thread calls notify or notifyAll on that same object the first thread changes state from WAITING to BLOCKED,Notified thread does NOT automatically reacquire the lock or become RUNNABLE, in fact it must fight for the lock with all other blocked threads.

WAITING and BLOCKED states both prevent thread from running, but they are very different.

WAITING threads must be explicitly transformed to BLOCKED threads by a notify from some other thread.

WAITING never goes directly to RUNNABLE.

When RUNNABLE thread releases the lock (by leaving monitor or by waiting) one of BLOCKED threads automatically takes its place.

So to summarize, thread acquires the lock when it enters synchronized method or when it reenters the synchronized method after the wait.

public synchronized guardedJoy() {    // must get lock before entering here    while(!joy) {        try {            wait(); // releases lock here            // must regain the lock to reentering here        } catch (InterruptedException e) {}    }    System.out.println("Joy and efficiency have been achieved!");}


I've prepared a small test class (some very dirty code, sorry) to demonstrate that wait actually releases the lock.

public class Test {    public static void main(String[] args) throws Exception {        testCuncurrency();    }    private static void testCuncurrency() throws InterruptedException {        Object lock = new Object();        Thread t1 = new Thread(new WaitTester(lock));        Thread t2 = new Thread(new WaitTester(lock));        t1.start();        t2.start();        Thread.sleep(15 * 1000);        synchronized (lock) {            System.out.println("Time: " + new Date().toString()+ ";" + "Notifying all");            lock.notifyAll();        }    }    private static class WaitTester implements Runnable {        private Object lock;        public WaitTester(Object lock) {            this.lock = lock;        }        @Override        public void run() {            try {                synchronized (lock) {                    System.out.println(getTimeAndThreadName() + ":only one thread can be in synchronized block");                    Thread.sleep(5 * 1000);                    System.out.println(getTimeAndThreadName() + ":thread goes into waiting state and releases the lock");                    lock.wait();                    System.out.println(getTimeAndThreadName() + ":thread is awake and have reacquired the lock");                    System.out.println(getTimeAndThreadName() + ":syncronized block have finished");                }            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    private static String getTimeAndThreadName() {        return "Time: " + new Date().toString() + ";" + Thread.currentThread().getName();    }}

Running this class on my machine returns next result:

Time: Tue Mar 29 09:16:37 EEST 2016;Thread-0:only one thread can be in synchronized blockTime: Tue Mar 29 09:16:42 EEST 2016;Thread-0:thread goes into waiting state and releases the lockTime: Tue Mar 29 09:16:42 EEST 2016;Thread-1:only one thread can be in synchronized blockTime: Tue Mar 29 09:16:47 EEST 2016;Thread-1:thread goes into waiting state and releases the lockTime: Tue Mar 29 09:16:52 EEST 2016;Notifying allTime: Tue Mar 29 09:16:52 EEST 2016;Thread-1:thread is awake and have reacquired the lockTime: Tue Mar 29 09:16:57 EEST 2016;Thread-1:syncronized block have finishedTime: Tue Mar 29 09:16:57 EEST 2016;Thread-0:thread is awake and have reacquired the lockTime: Tue Mar 29 09:17:02 EEST 2016;Thread-0:syncronized block have finished


wait :: is part of java.lang.Object class, so we can call this method only on the object. calling this needs monitor(lock) on that object, else IllegalMonitorStateException will be thrown, e.g) Thread.currentThread().wait() will throw this exception at below code.

   Example1   public void doSomething() {                                          Line 1        synchronized(lockObject) { //lock acquired                      Line 2            lockObject.wait();     // NOT Thread.currentThread().wait() Line 3        }    }

Now calling wait at Line 3 will release the lock acquired at Line 2. So any other thread entered into Line 1 and waiting to acquire lock on lockObject will acquire this lock and proceeds.

Now let us consider this Example2; here only lockObject2 lock is released, and still current thread holds lockObject1 lock. This will leads to deadlock; So user should be more careful on this case.

   Example2         public void doSomething() {                                     Line 1             synchronized(lockObject1) { //lock1 acquired               Line 2                 synchronized(lockObject2) { //lock2 acquired           Line 3                     lockObject2.wait();                                Line 4                 }             }        }

If this wait is replaced with sleep, yield, or join they dont have cabaility to release the lock. Only wait can release lock that it holds.

Just cautious on t1.sleep()/t1.yield() where are static api's and always action will be performed on the currentThread not on thread t1.

Then let us understand what is the difference between suspend and these api's sleep, yield, join; because suspend is deprecated to avoid situation of the thread is holding the lock which will leads to deadlock when it is in suspended (not running state) for undefined time. This is the same behavior for other apis as well.

The answer is suspend/resume will be performed on other threads, like t1.suspend() where as these api's are suspending the Thread.currentThread(). Hence user has caution note on taking care of not holding any locks before calling these api's to avoid deadlock. This is not the case when calling suspend. The callee thread don't know about the caller thread (lock) state on which it is going to perform suspend, hence deprecated.