如何避免在Reentrantlock上使用锁中断时出现IllegalMonitorStateException

时间:2021-03-31 06:54:07

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

我想用ReentrantLock替换一个经过语法处理的块,以支持等待锁的中断。为此,我使用lockInterruptibly()方法和惯用的try/finally块:

private ReentrantLock lock = new ReentrantLock();

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

The problem is that the finally ofcourse also happens when the InterruptedException happens. This results in an IllegalMonitorStateException, because the lock is not held by the current thread.

问题是,当InterruptedException发生时,最终也会发生。这会导致一个IllegalMonitorStateException异常,因为锁不是由当前线程持有的。

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();
        }
    }
}
}

It 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.java:312)
    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:597)
    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:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Interrupting thread main

Any idea on what the best way is to avoid this?

你知道最好的方法是什么吗?

2 个解决方案

#1


18  

the lockInterruptibly() call should be outside the finally block. note, this is always try for use the Lock API (whether you use lock() or lockInterruptibly()), as you don't want to do the "unlock" work unless you have acquired the lock.

锁中断()调用应该在finally块之外。注意,这总是尝试使用Lock API(不管您是使用Lock()还是lockInterruptibly())),因为除非您已经获得了锁,否则不希望进行“解锁”工作。

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

#2


2  

Simply using a boolean-flag should take care of this:

简单地使用boolean-flag应该注意这一点:

private ReentrantLock lock = new ReentrantLock();

boolean lockAcquired = false;

try
{
  lock.lockInterruptably();
  lockAcquired = true;
}
catch( InterruptedException e )
{
  Thread.currentThread.interrupt();
}
finally
{
  if(lockAcquired)
  {
    lock.unlock();
  }
}

#1


18  

the lockInterruptibly() call should be outside the finally block. note, this is always try for use the Lock API (whether you use lock() or lockInterruptibly()), as you don't want to do the "unlock" work unless you have acquired the lock.

锁中断()调用应该在finally块之外。注意,这总是尝试使用Lock API(不管您是使用Lock()还是lockInterruptibly())),因为除非您已经获得了锁,否则不希望进行“解锁”工作。

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

#2


2  

Simply using a boolean-flag should take care of this:

简单地使用boolean-flag应该注意这一点:

private ReentrantLock lock = new ReentrantLock();

boolean lockAcquired = false;

try
{
  lock.lockInterruptably();
  lockAcquired = true;
}
catch( InterruptedException e )
{
  Thread.currentThread.interrupt();
}
finally
{
  if(lockAcquired)
  {
    lock.unlock();
  }
}