2012-01-17 19 views
6

Desidero sostituire un blocco syncronized con un ReentrantLock per supportare l'interruzione di attesa del blocco. Per questo, io uso il metodo lockInterruptibly() e idiomatica try/blocco finally:Come evitare IllegalMonitorStateException quando si utilizza lockInterruptibly su Reentrantlock

private ReentrantLock lock = new ReentrantLock(); 

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

Il problema è che il finalmente naturalmente avviene anche quando l'InterruptedException succede. Ciò risulta in un IllegalMonitorStateException, perché il blocco non è trattenuto dal thread corrente.

Questo semplice programma dimostra questo:

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

Esso stampa questa uscita:

 
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 

Qualche idea su ciò che il modo migliore è quello di evitare questo?

risposta

18

la chiamata lockInterruptibly() dovrebbe essere al di fuori il blocco finally. nota, questo è sempre provare per utilizzare l'API Lock (se si utilizza lock() o lockInterruptibly()), come si non si desidera eseguire il lavoro di "sblocco" a meno che non è stato acquisito il blocco.

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

qualcuno si preoccupa di commentare il downvote? – jtahlborn

+0

Inizialmente stavo usando 'isHeldByCurrentThread', ma dopo aver letto tutti i commenti, penso che questa versione sia l'unica veramente corretta. –

+0

@WimDeblauwe a quali commenti ti riferisci, e perché pensi che l'uso di 'isHeldByCurrentThread' non sia corretto? –

2

semplicemente utilizzando un booleano-bandiera dovrebbe prendersi cura di questo:

private ReentrantLock lock = new ReentrantLock(); 

boolean lockAcquired = false; 

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