Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a synchronized block/method be interrupted?

While I know the theoretical differences between Re-EntrantLocks and synchronized, I'm confused to the below point.

See this statement from an article on Javarevisited comparing synchronized and Lock objects:

One more worth noting difference between ReentrantLock and synchronized keyword in Java is, ability to interrupt Thread while waiting for Lock. In case of synchronized keyword, a thread can be blocked waiting for lock, for an indefinite period of time and there was no way to control that. ReentrantLock provides a method called lockInterruptibly(), which can be used to interrupt thread when it is waiting for lock. Similarly tryLock() with timeout can be used to timeout if lock is not available in certain time period.

As per the above statement, I did try interrupting the Thread waiting() on synchronized method (i.e blocking wait) and it did throw an InterruptedException. But this behavior is contradictory with what is stated in the above statement.

// this method is called from inside run() method of every thread. 
public synchronized int getCount() {
        count++;
        try {
            Thread.sleep(3000);
            System.out.println(Thread.currentThread().getName() + " gets " + count);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return count;
}

....
....
t1.start();
t2.start();
t3.start();
t4.start();

t2.interrupt();

Here is the output that I got :

Thread 1 gets 1
Thread 4 gets 2
Thread 3 gets 3  
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at locks.SynchronizedLockInterrupt.getCount(SynchronizedLockInterrupt.java:10)  
    at locks.SynchronizedLockInterrupt$2.run(SynchronizedLockInterrupt.java:35)  
    at java.lang.Thread.run(Unknown Source)  

I'm confused if my example is not correct or the quoted statement about synchronized() is incorrect?

like image 353
Saurabh Gokhale Avatar asked Aug 23 '14 11:08

Saurabh Gokhale


2 Answers

Without the rest of the code this question might not be fully answered. What, I think, you're being confused with here is that you're seeing that, whilst the code would imply you cannot "interrupt" a thread that's blocked on a synchronized lock you are seeing that your count variable seems to be unaffected by the thread which is supposed to have entered into this method.

Important to note that you can technically "interrupt" a blocked thread, as in you can call interrupt() on it and this will set the interrupted flag. Just because a Thread has the interrupted flag set does not mean that it cannot execute any more code. Simply, when it get's to the next code that checks for an interrupted state, that code will likely throw an InterruptedException whilst clearing the flag at the same time. If the person catching the exception intends to do more work, it's their (almost moral) duty to re-set the flag or throw the same.

So, yes, in your example, you are catching the exception that has been thrown by .sleep() on entry, likely before the thread was sleep-ed, you then print the stack trace that proves that.

The outstanding question that might be causing confusion for you; why, then, did my count not increment if this code was allowed to run until the .sleep() method call?

The answer is that the count variable was incremented, you just didn't see the result.

synchronized in Java does not guarantee order and can lead to starvation so t2 just happened to be executed last and you never checked the count before you slept to see that it was already 3

So to answer your question, the documentation is correct and the behaviour is correct.

Interrupting a thread which is waiting "uninterruptedly" on a Lock , ReentrantLock or synchronized block will merely result in the thread waking up and seeing if it's allowed to take the lock yet, by whatever mechanism is in place in the defining lock, and if it cannot it parks again until it is interrupted again or told it can take the lock. When the thread can proceed it simply proceeds with its interrupted flag set.

Contrast to lockInterruptibly where, actually, if you are interrupted, you do not ever get the lock, and instead you "abort" trying to get the lock and the lock request is cancelled.

lock and lockInterruptibly can be mixed use on the same ReentrantLock as the lock will manage the queue and skip requests that were CANCELLED by a finally statement because they were interrupted when waiting on a lock.

In summary:

  • You can almost always interrupt a thread.
  • The interrupt flag is usually only cleared on a thread by code that documents that it clears the flag when throwing the InterruptedException , but not all code documents this (lockInterruptibly on ReentrantLock does, but not the same on AbstractQueuedSynchronizer which powers the lock).
  • Interrupting a thread has different behaviour depending on what it is doing at the time;
    • A parked thread will be un-parked and have it's flag set, usually then cleared
    • A thread waiting on a lock / synchronized block will eventually get into the code but with interrupted flag set
    • A thread waiting on a lockInterruptibly or a get on a future etc will be unparked and behave as documented, aborting the lock acquisition.
like image 161
iZian Avatar answered Oct 01 '22 08:10

iZian


synchronized is an intrinsic lock which is beyond the control of JDK.

Synchronization is built around an internal entity known as the intrinsic lock or monitor lock. (The API specification often refers to this entity simply as a "monitor.") Intrinsic locks play a role in both aspects of synchronization: enforcing exclusive access to an object's state and establishing happens-before relationships that are essential to visibility.

When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object and releases it when the method returns. The lock release occurs even if the return was caused by an uncaught exception.

In your example, you are actually interrupting the sleep as JDK doc mentions.

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

More details about how interrupt() works.

Many methods that throw InterruptedException, such as sleep, are designed to cancel their current operation and return immediately when an interrupt is received.

like image 24
Hearen Avatar answered Oct 01 '22 10:10

Hearen