Avoiding an IllegalMonitorState exception when using lockInterruptibly on Reentrantlock

I want to replace the syncronized block with syncronized in ReentrantLock to support interruption of waiting for the lock. To do this, I use the lockInterruptibly() method and the idiomatic try / finally block:

 private ReentrantLock lock = new ReentrantLock(); try { lock.lockInterruptably(); } catch( InterruptedException e ) { Thread.currentThread.interrupt(); } finally { lock.unlock(); } 

The problem is what ultimately happens as well when an InterruptedException occurs. This raises an IllegalMonitorStateException because the lock is not held by the current thread.

This simple program proves this:

 public class LockTest { public static void main( String[] args ) { System.out.println("START"); Thread interruptThread = new Thread( new MyRunnable( Thread.currentThread() ) ); interruptThread.start(); ReentrantLock lock = new ReentrantLock(); Thread takeLockThread = new Thread( new TakeLockRunnable( lock ) ); takeLockThread.start(); try { Thread.sleep( 500 ); System.out.println("Trying to take lock on thread " + Thread.currentThread().getName()); lock.lockInterruptibly(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } System.out.println( "DONE"); } private static class MyRunnable implements Runnable { private Thread m_thread; private MyRunnable( Thread thread) { m_thread = thread; } @Override public void run() { try { Thread.sleep( 1000 ); } catch (InterruptedException e) { // ignore } System.out.println( "Interrupting thread " + m_thread.getName() ); m_thread.interrupt(); } } private static class TakeLockRunnable implements Runnable { private ReentrantLock m_lock; public TakeLockRunnable( ReentrantLock lock ) { m_lock = lock; } @Override public void run() { try { System.out.println("Taking lock on thread " + Thread.currentThread().getName()); m_lock.lock(); Thread.sleep( 20000 ); } catch (Exception e) { e.printStackTrace(); } finally { m_lock.unlock(); } } } } 

He prints this output:

  START
 Taking lock on thread Thread-1
 Trying to take lock on thread main
 java.lang.InterruptedException
     at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly (AbstractQueuedSynchronizer.java:877)
     at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly (AbstractQueuedSynchronizer.java:1201)
     at java.util.concurrent.locks.ReentrantLock.lockInterruptibly (ReentrantLock.javahaps12)
     at LockTest.main (LockTest.java:25)
     at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
     at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:39)
     at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:25)
     at java.lang.reflect.Method.invoke (Method.java∗97)
     at com.intellij.rt.execution.application.AppMain.main (AppMain.java:120)
 Exception in thread "main" java.lang.IllegalMonitorStateException
     at java.util.concurrent.locks.ReentrantLock $ Sync.tryRelease (ReentrantLock.java:127)
     at java.util.concurrent.locks.AbstractQueuedSynchronizer.release (AbstractQueuedSynchronizer.java:1239)
     at java.util.concurrent.locks.ReentrantLock.unlock (ReentrantLock.java:431)
     at LockTest.main (LockTest.java:32)
     at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
     at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:39)
     at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:25)
     at java.lang.reflect.Method.invoke (Method.java∗97)
     at com.intellij.rt.execution.application.AppMain.main (AppMain.java:120)
 Interrupting thread main

Any idea what is the best way to avoid this?

+6
source share
2 answers

the lockInterruptibly() call must be outside the finally block. Note that this is always an attempt to use the API Lock (whether you use lock() or lockInterruptibly() ), since you do not want to “unlock” it unless you have acquired a lock.

 try { lock.lockInterruptibly(); try { // do locked work here } finally { lock.unlock(); } } catch( InterruptedException e ) { Thread.currentThread.interrupt(); } 
+18
source

A simple use of a boolean flag should take care of this:

 private ReentrantLock lock = new ReentrantLock(); boolean lockAcquired = false; try { lock.lockInterruptably(); lockAcquired = true; } catch( InterruptedException e ) { Thread.currentThread.interrupt(); } finally { if(lockAcquired) { lock.unlock(); } } 
+2
source

Source: https://habr.com/ru/post/906212/


All Articles