IllegalMonitorStateException di Java

1. Gambaran keseluruhan

Dalam tutorial ringkas ini, kita akan belajar mengenai java.lang.IllegalMonitorStateException.

Kami akan membuat aplikasi pengirim-penerima sederhana yang membuang pengecualian ini. Kemudian, kita akan membincangkan kemungkinan cara mencegahnya. Akhirnya, kami akan menunjukkan cara melaksanakan kelas pengirim dan penerima ini dengan betul.

2. Bilakah Ia Dilempar?

The IllegalMonitorStateException berkaitan dengan multithreading pengaturcaraan Java. Sekiranya kita mempunyai monitor yang ingin kita selaraskan, pengecualian ini dilemparkan untuk menunjukkan bahawa utas cuba menunggu atau memberitahu benang lain yang menunggu di monitor itu, tanpa memilikinya. Dengan kata yang lebih mudah, kita akan mendapat pengecualian ini jika kita memanggil salah satu kaedah tunggu () , memberitahu (), atau notifikasiAll () dari kelas Objek di luar blok yang disegerakkan .

Sekarang mari kita bina contoh yang melemparkan IllegalMonitorStateException . Untuk ini, kami akan menggunakan kaedah tunggu () dan notifyAll () untuk menyegerakkan pertukaran data antara pengirim dan penerima.

Pertama, mari kita lihat kelas Data yang menyimpan mesej yang akan kita hantar:

public class Data { private String message; public void send(String message) { this.message = message; } public String receive() { return message; } }

Kedua, mari buat kelas pengirim yang melemparkan IllegalMonitorStateException apabila dipanggil . Untuk tujuan ini, kami akan memanggil kaedah notifyAll () tanpa membungkusnya dalam blok yang disegerakkan :

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(); } } }

Penerima juga akan membuang IllegalMonitorStateException. Sama seperti contoh sebelumnya, kami akan membuat panggilan ke kaedah tunggu () di luar blok yang disegerakkan :

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; } }

Akhirnya, mari kita mulakan kedua-dua kelas dan hantarkan mesej di antara mereka:

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); }

Apabila kita cuba untuk menjalankan ini sekeping kod, kita akan menerima IllegalMonitorStateException dari kedua-dua UnsynchronizedReceiver dan UnsynchronizedSender kelas:

[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. Cara Memperbaikinya

Untuk menyingkirkan IllegalMonitorStateException, kita perlu membuat setiap panggilan untuk menunggu () , memberitahu (), dan memberi tahu Semua () kaedah dalam blok yang disegerakkan . Dengan ini, mari kita lihat bagaimana pelaksanaan kelas Pengirim yang betul :

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(); } } }

Perhatikan bahawa kami menggunakan blok yang disegerakkan pada contoh Data yang sama yang kemudian kami namakan kaedah notifyAll () .

Mari betulkan Penerima dengan cara yang sama:

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; } }

Sekiranya kita sekali lagi membuat kedua-dua kelas dan cuba menghantar mesej yang sama di antara mereka, semuanya berfungsi dengan baik, dan tidak ada pengecualian yang dilemparkan.

4. Kesimpulan

Dalam artikel ini, kami mengetahui apa yang menyebabkan IllegalMonitorStateException dan bagaimana mencegahnya.

Seperti biasa, kodnya tersedia di GitHub.