2012-01-17 25 views
6

Tôi muốn thay thế khối syncronized bằng ReentrantLock để hỗ trợ gián đoạn chờ khóa. Đối với điều này, tôi sử dụng phương pháp lockInterruptibly() và thử thành ngữ/khối finally:Cách tránh IllegalMonitorStateException khi sử dụng lockInterruptibly trên Reentrantlock

private ReentrantLock lock = new ReentrantLock(); 

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

Vấn đề là cuối cùng cũng ofcourse cũng xảy ra khi InterruptedException xảy ra. Điều này dẫn đến một số IllegalMonitorStateException, bởi vì khóa không được giữ bởi luồng hiện tại.

chương trình đơn giản này chứng tỏ điều này:

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 in sản lượng này:

 
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 

Bất kỳ ý tưởng về những gì cách tốt nhất là để tránh điều này?

Trả lời

18

cuộc gọi lockInterruptibly() nên ngoài các khối finally. lưu ý, điều này luôn được thử sử dụng API Lock (cho dù bạn sử dụng lock() hoặc lockInterruptibly()), vì bạn không muốn thực hiện công việc "mở khóa" trừ khi bạn đã có khóa.

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

bất kỳ ai quan tâm để nhận xét về downvote? – jtahlborn

+0

Lần đầu tiên tôi sử dụng 'isHeldByCurrentThread', nhưng sau khi đọc tất cả các nhận xét, tôi nghĩ phiên bản này là phiên bản duy nhất thực sự chính xác. –

+0

@WimDeblauwe mà bạn đang đề cập đến, và tại sao bạn nghĩ rằng sử dụng 'isHeldByCurrentThread' là không chính xác? –

2

Đơn giản chỉ cần sử dụng một boolean-cờ nên chăm sóc này:

private ReentrantLock lock = new ReentrantLock(); 

boolean lockAcquired = false; 

try 
{ 
    lock.lockInterruptably(); 
    lockAcquired = true; 
} 
catch(InterruptedException e) 
{ 
    Thread.currentThread.interrupt(); 
} 
finally 
{ 
    if(lockAcquired) 
    { 
    lock.unlock(); 
    } 
} 
Các vấn đề liên quan