Search This Blog

Thursday, April 28, 2011

Object.wait() vs. Thread.sleep()

Another day in the office, another thread dump to be analyzed. We got a thread dump that had a pretty weird pattern: it was waiting on a monitor, but it was also locking the Object. Welcome to the fascinating world of deadlocks!
Scanning the code, everything seems fine. Unless.... the Object.wait(time) method would void the synchronized block. Searching through the Java docs, we find:

* This method causes the current thread (call it T) to
    * place itself in the wait set for this object and then to relinquish
    * any and all synchronization claims on this object.

So the bloody wait method is really voiding the synchronized block contract. To put it into a simple scenario, we have a synchronized block surrounding the login section, in order to prevent multiple locking of the same User object. But, the User clicks the login button repeatedly and impatiently. The first Thread takes ownership, creates the user object and ends. The next ones try to lock the object, can't do that and call wait. And since wait() will give up ownership of the block, a new Thread may claim the synchronized region, thus resulting in deadlock.
The immediate solution to this problem is to useThread.sleep(time). The sleep method will keep the lock on the synchronized block, preventing attempts of overtaking. :)

2 comments:

  1. Thread.sleep() sends the current thread into the "Not Runnable" state for some amount of time. The thread keeps the monitors it has aquired -- i.e. if the thread is currently in a synchronized block or method no other thread can enter this block or method. If another thread calls t.interrupt() it will wake up the sleeping thread.
    Note that sleep is a static method, which means that it always affects the current thread (the one that is executing the sleep method). A common mistake is to call t.sleep() where t is a different thread; even then, it is the current thread that will sleep, not the t thread.

    t.suspend() is deprecated. Using it is possible to halt a thread other than the current thread. A suspended thread keeps all its monitors and since this state is not interruptable it is deadlock prone.

    object.wait() sends the current thread into the "Not Runnable" state, like sleep(), but with a twist. Wait is called on a object, not a thread; we call this object the "lock object." Before lock.wait() is called, the current thread must synchronize on the lock object; wait() then releases this lock, and adds the thread to the "wait list" associated with the lock. Later, another thread can synchronize on the same lock object and call lock.notify(). This wakes up the original, waiting thread. Basically, wait()/notify() is like sleep()/interrupt(), only the active thread does not need a direct pointer to the sleeping thread, but only to the shared lock object.

    ReplyDelete
  2. Thread.sleep(long milliseconds): sends the current thread into Non-Runnable state for the specified amount of time. But, this doesn’t cause the thread to loose ownership of the acquired monitors. So, if the current thread is into a synchronized block/method, then no other thread will be able to enter that block/method. This method throws ‘InterruptedException’ if another thread interrupts it. There is another variant of the ‘sleep()’ method as well where it accepts two arguments – one, long milliseconds, and second, int nanoseconds. Evidently, it causes the current thread to sleep for the specified number of milliseconds plus the specified number of nanoseconds. The second argument ‘int nanoseconds’ can acquire a value of the range 0-999999. Another noticeable difference between the two variants is that sleep(long milliseconds, int nanoseconds) throws an IllegalArgumentException if either the value of milliseconds is negative or the value of nanoseconds is not in the range 0-999999. As is the case with the first variant, it throws InterruptedException is another thread interrupts it. Common Error: both these methods are static, so even if you try to call ‘t.sleep(…)’ on a different thread, it’ll be called on the current thread only and not on the ‘t’ thread. In fact, one should avoid calling any static method on an object reference. wait() method: There are three variants of this method in the ‘Object’ class:- public final void wait(long timeout) public final void wait(long timeout, int nanoseconds) public final void wait() All the three methods throw InterruptedException & IllegalMonitorStateException. The first two may also throw IllegalArgumentException. The wait() method also sends the current thread into Non-Runnable state like the sleep() method. But, the difference between the two is that in case of ‘wait()’ the locks are released before going into Non-Runnable state. Another apparent difference is that ‘wait()’ is an instance method, while sleep() is a static method. The method ‘wait()’ should be called for an object only when the current thread has already acquired lock for that object. This causes the current thread to wait either another thread invokes the ‘notify()’ method or the ‘notifyAll()’ method for this object, or a specified amount of time has elapsed and after that the thread starts participating in thread scheduling process to acquire the monitor of the object to proceed further. The wait() method causes the current thread to place itself in the wait set for this object and then to relinquish any and all synchronization claims on this object. After the execution of this method invocation, the thread becomes disabled for any scheduling purposes and lies dormant until one of the following things happen:- Any other thread invokes ‘notify()’ method this object and the thread under consideration is arbitrarily chosen as the thread to be awakened. Any other thread invokes ‘notifyAll()’ for this object. Any other thread interrupts the thread under consideration. The specified amount of time has elapsed (in case first two variants of wait() are used) After any of the four above mentioned events happens, the thread is removed from the wait set for this object and re-enabled for thread scheduling. It’ll compete for the rights to synchronize on the object in an usual manner and it’ll keep doing this until it acquires control on the object and gains all the synchronization claims on the object, which it had acquired at the time of ‘wait()’ invocation. This means that after the return from wait() method, the synchronization state of object and of the thread will be exactly the same as it was before the wait() invocation.

    ReplyDelete