Выброс IllegalMonitorStateException в StampedLock
Изучая StampedLock тестировал приведенный ниже код:
public class StampedLockDemo {
public static void main(String[] args) {
StampedLock sl = new StampedLock();
ExecutorService executor = Executors.newFixedThreadPool(2);
// Runnable as lambda - optimistic read
Runnable r1 = () -> {
long stamp = sl.tryOptimisticRead();
try {
System.out.println("In optimistic lock " + sl.validate(stamp));
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("In optimistic lock " + sl.validate(stamp));
} finally {
sl.unlock(stamp);
}
};
new Thread(r1).start();
//executor.submit(r1);
//executor.shutdown();
}
}
Если я создам пул потоков и воспользуюсь интерфейсом ExecutorService: executor.submit(r1);, то данный код отработает и не выбросит исключение.
Однако если создать и запустить объект потока : new Thread(r1).start();, то, дойдя до разблокировки, выбросится исключение
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.base/java.util.concurrent.locks.StampedLock.unlockRead(StampedLock.java:683)
at java.base/java.util.concurrent.locks.StampedLock.unlock(StampedLock.java:698)
at Reentrantlock.StampedLockDemo.lambda$main$0(StampedLockDemo.java:25)
at java.base/java.lang.Thread.run(Thread.java:833)
Не могу понять в чем разница между двумя этими реализациями: почему первый не выбросит исключение, а второй (дойдя до sl.unlock(stamp);) - выбросит. Помогите, пожалуйста, разобраться
Ответы (1 шт):
Автор решения: Michel_T.
→ Ссылка
executor.submit(r1);тоже бросает исключение, просто оно "съедается" самим экзекьютором. Даже если ты поменяешь Runnable чтобы он гарантированно бросал исключение, ты его не увидишь:Runnable r1 = () -> {throw new RuntimeException();}
Попробуй "получить" значение от запущенного потока и ты увидишь ровно то же исключение, что и для Thread:
Future<?> result = executor.submit(r1);
executor.shutdown();
result.get(); // IllegalMonitorStateException
- Исключение бросается, потому что
tryOptimisticReadне берёт lock. Он нужен только чтобы проверить, что со времени вызовы этого метода, не происходила write-блокировка, т.е. ты берёшь stamp, потом читаешь значения необходимых тебе переменных и проверяешь что не происходила ли write-блокировка tryOptimisticRead и текущим местом. Если write брался, то validate вернёт false. Это значит, что прочитанные значения невалидные, нужно читать снова.
System.out.println("In optimistic lock " + sl.validate(stamp)); // true
writeLockStamp = sl.writeLock();
sl.unlock(writeLockStamp); // отпустили write-блокировку
System.out.println("In optimistic lock after exclusive lock: " + sl.validate(stamp)); // false