В этом уроке мы изучим IllegalMonitorStateException и когда оно выбрасывается с примерами. Мы также увидим, как можно предотвратить выбрасывание этого исключения в нашем коде, а также некоторые из лучших практик, которым нужно следовать в многопоточных приложениях.
1. Класс Java IllegalMonitorStateException
Класс IllegalMonitorStateException присутствует в пакете java.lang и существует с версии Java 1.0. Он расширяетКласс RuntimeException; следовательно, это непроверяемое исключение, и его не нужно объявлять в предложении throws метода или конструктора.
Конструкторы, определенные в этом классе:
- IllegalMonitorStateException(): создает экземпляр класса IllegalMonitorStateException, устанавливая null в качестве его сообщения.
- IllegalMonitorStateException(String message): создает экземпляр класса IllegalMonitorStateException, используя указанное сообщение.
2. Что вызывает IllegalMonitorStateException
Для межпоточной коммуникации(когда 2 потока взаимодействуют друг с другом) потоки должны использовать методы wait(), notify() и notifyAll(). Класс Thread наследует эти методы от класса Object.
Поток должен получить блокировку объекта, чтобы вызвать эти методы на этом объекте. Если поток пытается вызвать методы wait(), notify() и notifyAll() на объекте без получения блокировки этого объекта или вне синхронизированного блока, программа выдает IllegalMonitorStateException.
Давайте теперь рассмотрим пример того, как поток вызывает IllegalMonitorStateException. В этом примере мы создадим ожидающий поток и уведомляющий поток.
- WaitingThread вызывает метод wait() и переходит в состояние ожидания, пока какой-либо другой поток не вызовет метод notify() и не уведомит его.
- NotifyingThread вызывает метод notify() и уведомляет ожидающий поток о необходимости повторного начала обработки.
public class IllegalMonitorStateExceptionDemo{//This object is used for synchronization among threads.public final static Object obj = new Object();public static class WaitingThread extends Thread {@Overridepublic void run() {try {//Calling wait() method outside of synchronized areaobj.wait(); // Raises IllegalMonitorStateException}catch(InterruptedException ex) {System.out.println(Thread.currentThread().getName() + " gets Interrupted");}}}public static class NotifyingThread extends Thread {@Overridepublic void run() {try {// Thread sleep for 5 secThread.sleep(5000);// Calling notify() outside of synchronized areaobj.notify(); // Raises IllegalMonitorStateException}catch(InterruptedException ex) {System.err.println(Thread.currentThread().getName() + " gets Interrupted");}}}}
Попробуйте запустить оба потока для проверки.
public static void main(String[] args) {WaitingThread waitingThread = new WaitingThread();NotifyingThread notifyingThread = new NotifyingThread();waitingThread.start();notifyingThread.start();}
Мы получим исключение IllegalMonitorStateException в консоли.
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException: current thread is not ownerat java.base/java.lang.Object.wait(Native Method)at java.base/java.lang.Object.wait(Object.java:338)at com.howtodoinjava.concurrency.IllegalMonitorStateExceptionDemo $WaitingThread.run(IllegalMonitorStateExceptionDemo.java:23)Exception in thread "Thread-1" java.lang.IllegalMonitorStateException: current thread is not ownerat java.base/java.lang.Object.notify(Native Method)at com.howtodoinjava.concurrency.IllegalMonitorStateExceptionDemo $NotifyingThread.run(IllegalMonitorStateExceptionDemo.java:39)
3. Разрешение IllegalMonitorStateException
IllegalMonitorStateException разрешается, когда поток вызывает методы wait(), notify() и notifyAll() для объекта после получения блокировки этого объекта, что означает, что он должен вызывать эти методы только внутри синхронизированного блока.
В приведенном выше примере, если оба потока ожидания и уведомления вызывают wait() и notify() в синхронизированном блоке, то мы не получим IllegalMonitorStateException в нашем коде.
Таким образом, правильный способ вызова этих методов без этого исключения выглядит следующим образом:
public static class WaitingThread extends Thread {@Overridepublic void run() {try {//Obtain lock using the synchronized keywordsynchronized(obj){//Calling wait() inside synchronized blockobj.wait();}}catch(InterruptedException ex) {System.err.println(Thread.currentThread().getName() + " gets Interrupted");}}}public static class NotifyingThread extends Thread {@Overridepublic void run() {try {Thread.sleep(5000);//Obtain lock firstsynchronized(obj){//Calling notify() inside synchronized blockobj.notify();}}catch(InterruptedException ex) {System.err.println(Thread.currentThread().getName() + " gets Interrupted");}}}
Теперь, если мы запустим программу, она успешно завершится.
4. Лучшие практики
- Начиная с версии Java 1.5, у нас есть пакет java.util.concurrent, который содержит различные новые классы и фреймворки параллелизма, упрощающие нашу работу в многопоточной среде.
- Для межпоточного взаимодействия вместо использования старого способа использования методов wait() и notify() мы можем использовать BlockingQueue, представленный в пакете java.util.concurrent.
- Интерфейс BlockingQueue представляет собой очередь, в которую можно потокобезопасно помещать и из которой можно извлекать элементы, пока два потока взаимодействуют друг с другом, что, в свою очередь, также позволяет избежать исключений IllegalMonitorStateException.
- Интерфейс Lock и класс ReentrantLock обеспечивают больший контроль над параллелизмом по сравнению с синхронизированным блоком. Используя его, поток может попытаться заблокировать без ожидания, что исключает вероятность возникновения ситуации взаимоблокировки.
- Мы можем использовать другие классы, такие как CountDownLatch, CyclicBarrier, Exchanger, Phaser, SynchronousQueue, для работы с несколькими потоками в многопоточном приложении.
5. Заключение
В этой статье мы узнали о IllegalMonitorStateException, каковы причины этого и как предотвратить это в нашем коде. Мы также увидели некоторые из лучших практик использования новых классов параллелизма, которые могут помочь избежать IllegalMonitorStateException.