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?
qualcuno si preoccupa di commentare il downvote? – jtahlborn
Inizialmente stavo usando 'isHeldByCurrentThread', ma dopo aver letto tutti i commenti, penso che questa versione sia l'unica veramente corretta. –
@WimDeblauwe a quali commenti ti riferisci, e perché pensi che l'uso di 'isHeldByCurrentThread' non sia corretto? –