Разрешение IllegalMonitorStateException в Java

В этом уроке мы изучим 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.

Исходный код на Github

Прокрутить вверх