首页 文章

在Reentrantlock上使用lockInterruptibly时如何避免IllegalMonitorStateException

提问于
浏览
6

我想用 ReentrantLock 替换 syncronized 块以支持等待锁定的中断 . 为此,我使用 lockInterruptibly() 方法和惯用的try / finally块:

private ReentrantLock lock = new ReentrantLock();

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

问题是当InterruptedException发生时,也会发生最终的问题 . 这导致 IllegalMonitorStateException ,因为当前线程不保持锁定 .

这个简单的程序证明了这一点:

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

它打印此输出:

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

有什么最好的方法来避免这种想法吗?

2 回答

  • 19

    lockInterruptibly() 调用应该在finally块之外 . 请注意,这总是尝试使用 Lock API(无论您使用 lock() 还是 lockInterruptibly() ),因为除非您已获得锁定,否则不希望执行"unlock"工作 .

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

    只需使用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();
      }
    }
    

相关问题