1. 概要

この短いチュートリアルでは、 java.lang.IllegalMonitorStateException。 

この例外をスローする単純な送受信アプリケーションを作成します。 次に、それを防ぐための可能な方法について説明します。  最後に、これらの送信者クラスと受信者クラスを正しく実装する方法を示します。

2. いつ投げられますか?

IllegalMonitorStateException は、Javaでのマルチスレッドプログラミングに関連しています。 同期したいモニターがある場合、この例外がスローされ、スレッドがそのモニターを所有せずに待機しようとしたこと、またはそのモニターで待機している他のスレッドに通知しようとしたことを示します。 簡単に言うと、同期ブロックの外部でObjectクラスのwait()、notify()、notifyAll()メソッドのいずれかを呼び出すと、この例外が発生します。

IllegalMonitorStateExceptionをスローする例を作成してみましょう。 このために、 wait()メソッドと notifyAll()メソッドの両方を使用して、送信者と受信者の間のデータ交換を同期します。

まず、送信するメッセージを保持するDataクラスを見てみましょう。

public class Data {
    private String message;

    public void send(String message) {
        this.message = message;
    }

    public String receive() {
        return message;
    }
}

次に、をスローする送信者クラスを作成しましょう IllegalMonitorStateException 呼び出されたときこの目的のために、私たちは notifyAll() でラップせずにメソッド同期ブロック:

class UnsynchronizedSender implements Runnable {
    private static final Logger log = LoggerFactory.getLogger(UnsychronizedSender.class);
    private final Data data;

    public UnsynchronizedSender(Data data) {
        this.data = data;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000);

            data.send("test");

            data.notifyAll();
        } catch (InterruptedException e) {
            log.error("thread was interrupted", e);
            Thread.currentThread().interrupt();
        }
    }
}

レシーバーもスローします IllegalMonitorStateException。 前の例と同様に、 待つ() 外のメソッド同期ブロック:

public class UnsynchronizedReceiver implements Runnable {
    private static final Logger log = LoggerFactory.getLogger(UnsynchronizedReceiver.class);
    private final Data data;
    private String message;

    public UnsynchronizedReceiver(Data data) {
        this.data = data;
    }

    @Override
    public void run() {
        try {
            data.wait();
            this.message = data.receive();
        } catch (InterruptedException e) {
            log.error("thread was interrupted", e);
            Thread.currentThread().interrupt();
        }
    }

    public String getMessage() {
        return message;
    }
}

最後に、両方のクラスをインスタンス化し、それらの間でメッセージを送信しましょう。

public void sendData() {
    Data data = new Data();

    UnsynchronizedReceiver receiver = new UnsynchronizedReceiver(data);
    Thread receiverThread = new Thread(receiver, "receiver-thread");
    receiverThread.start();

    UnsynchronizedSender sender = new UnsynchronizedSender(data);
    Thread senderThread = new Thread(sender, "sender-thread");
    senderThread.start();

    senderThread.join(1000);
    receiverThread.join(1000);
}

このコードを実行しようとすると、UnsynchronizedReceiverクラスとUnsynchronizedSenderクラスの両方からIllegalMonitorStateExceptionを受け取ります。

[sender-thread] ERROR com.baeldung.exceptions.illegalmonitorstate.UnsynchronizedSender - illegal monitor state exception occurred
java.lang.IllegalMonitorStateException: null
	at java.base/java.lang.Object.notifyAll(Native Method)
	at com.baeldung.exceptions.illegalmonitorstate.UnsynchronizedSender.run(UnsynchronizedSender.java:15)
	at java.base/java.lang.Thread.run(Thread.java:844)

[receiver-thread] ERROR com.baeldung.exceptions.illegalmonitorstate.UnsynchronizedReceiver - illegal monitor state exception occurred
java.lang.IllegalMonitorStateException: null
	at java.base/java.lang.Object.wait(Native Method)
	at java.base/java.lang.Object.wait(Object.java:328)
	at com.baeldung.exceptions.illegalmonitorstate.UnsynchronizedReceiver.run(UnsynchronizedReceiver.java:12)
	at java.base/java.lang.Thread.run(Thread.java:844)

3. それを修正する方法

IllegalMonitorStateExceptionを取り除くには、同期ブロック内でwait()、notify()、notifyAll()メソッドをすべて呼び出す必要があります。これを念頭に置いて、 Senderクラスは次のようになります。

class SynchronizedSender implements Runnable {
    private final Data data;

    public SynchronizedSender(Data data) {
        this.data = data;
    }

    @Override
    public void run() {
        synchronized (data) {
            data.send("test");

            data.notifyAll();
        }
    }
}

後でnotifyAll()メソッドを呼び出す同じDataインスタンスでsynchronizedブロックを使用していることに注意してください。

同じ方法でReceiverを修正しましょう。

class SynchronizedReceiver implements Runnable {
    private static final Logger log = LoggerFactory.getLogger(SynchronizedReceiver.class);
    private final Data data;
    private String message;

    public SynchronizedReceiver(Data data) {
        this.data = data;
    }

    @Override
    public void run() {
        synchronized (data) {
            try {
                data.wait();
                this.message = data.receive();
            } catch (InterruptedException e) {
                log.error("thread was interrupted", e);
                Thread.currentThread().interrupt();
            }
        }
    }

    public String getMessage() {
        return message;
    }
}

両方のクラスを再度作成し、それらの間で同じメッセージを送信しようとすると、すべてが正常に機能し、例外はスローされません。

4. 結論

この記事では、IllegalMonitorStateExceptionの原因とそれを防ぐ方法を学びました。

いつものように、コードはGitHubから入手できます。