2012-01-17 89 views
6

我想用ReentrantLock替換syncronized塊以支持等待鎖的中斷。對於這一點,我用的是lockInterruptibly()方法和慣用的try/finally塊:如何避免IllegalMonitorStateException使用lockInterruptibly在可重入鎖

private ReentrantLock lock = new ReentrantLock(); 

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

的問題是,最終當InterruptedException的發生ofcourse也發生了。這導致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 

什麼最好的辦法的任何想法是避免這種情況?

回答

18

lockInterruptibly()調用應該是 finally塊。注意,這總是嘗試使用Lock API(無論你使用lock()還是lockInterruptibly()),因爲你不要想要做「解鎖」工作,除非你已經獲得鎖定。

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

任何人都喜歡評論downvote? – jtahlborn 2012-01-17 18:46:53

+0

我第一次使用'isHeldByCurrentThread',但在閱讀完所有評論之後,我認爲這個版本是唯一真正正確的版本。 – 2012-01-18 07:08:00

+0

@WimDeblauwe您提到哪些評論,以及爲什麼您認爲使用'isHeldByCurrentThread'是不正確的? – 2013-01-29 17:38:24

2

只需用一個布爾標誌應該照顧這:

private ReentrantLock lock = new ReentrantLock(); 

boolean lockAcquired = false; 

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