在本教程中,我们将 学习IllegalMonitorStateException以及它何时被抛出的例子。我们还将看到如何防止我们的代码抛出这种异常,以及在多线程应用程序中应遵循的一些最佳实践。
1.JavaIllegalMonitorStateException类
IllegalMonitorStateException 类存在于java.lang 包中 ,从 Java 1.0版本开始就存在了 。它扩展了 RuntimeException类;因此,它是一个未经检查的异常,不需要在方法或构造函数的throws 条款中声明。
在这个类中定义的构造函数。
IllegalMonitorStateException():创建一个IllegalMonitorStateException类的实例,设置null为其消息。IllegalMonitorStateException(String *message*):创建IllegalMonitorStateException类的一个实例,使用指定的消息。
2.导致IllegalMonitorStateException的原因
对于线程间的通信(2个线程互相通信),线程必须使用wait(),notify() 和notifyAll()方法。线程类从对象类中继承了这些方法。
一个线程应该获得对象的锁来调用该对象上的这些方法。如果一个Thread试图在没有获得对象的锁或在同步块之外调用wait(), notify() 和notifyAll() 方法,程序会抛出IllegalMonitorStateException。**
现在让我们看一个例子,看看线程 如何引发IllegalMonitorStateException。 在这个例子中,我们将创建一个WaitingThread和一个NotifyingThread。
- WaitingThread 调用
wait()方法并进入等待状态,直到有其他线程 调用notify()方法并通知它。 - NotifyingThread 调用
notify()方法,并通知等待中的Thread 重新开始处理。
public class IllegalMonitorStateExceptionDemo
{
//This object is used for synchronization among threads.
public final static Object obj = new Object();
public static class WaitingThread extends Thread {
@Override
public void run() {
try {
//Calling wait() method outside of synchronized area
obj.wait(); // Raises IllegalMonitorStateException
}
catch (InterruptedException ex) {
System.out.println(Thread.currentThread().getName() + " gets Interrupted");
}
}
}
public static class NotifyingThread extends Thread {
@Override
public void run() {
try {
// Thread sleep for 5 sec
Thread.sleep(5000);
// Calling notify() outside of synchronized area
obj.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 owner
at 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 owner
at java.base/java.lang.Object.notify(Native Method)
at com.howtodoinjava.concurrency.IllegalMonitorStateExceptionDemo $NotifyingThread.run(IllegalMonitorStateExceptionDemo.java:39)
3.解决IllegalMonitorStateException
IllegalMonitorStateException 解决的是当一个线程 在获得一个对象的锁后调用wait(),notify() 和notifyAll() 方法时,意味着它应该 只在synchronized 块内调用这些方法 。
在上面的例子中,如果等待和通知线程都在同步 块中调用wait()和notify() ,那么我们的代码中就不会出现IllegalMonitorStateException。
因此,正确的调用这些方法而不出现这个异常的方法是如下的。
public static class WaitingThread extends Thread {
@Override
public void run() {
try {
//Obtain lock using the synchronized keyword
synchronized(obj){
//Calling wait() inside synchronized block
obj.wait();
}
}
catch (InterruptedException ex) {
System.err.println(Thread.currentThread().getName() + " gets Interrupted");
}
}
}
public static class NotifyingThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(5000);
//Obtain lock first
synchronized(obj){
//Calling notify() inside synchronized block
obj.notify();
}
}
catch (InterruptedException ex) {
System.err.println(Thread.currentThread().getName() + " gets Interrupted");
}
}
}
现在,如果我们运行该程序,它就会成功完成。
4.最佳实践
- 自从Java 1.5版本以来,我们有了 java.util.concurrent包,它包含了各种新的并发类和框架 ,使我们在多线程环境中工作时更加轻松。
- 对于线程间的通信,我们可以使用java.util.concurrent包中的BlockingQueue来代替使用
wait()和notify()方法的老方法。 - BlockingQueue接口代表了一个队列,它是线程安全的,可以在两个线程相互通信时放入和取出元素,这反过来也避免了任何IllegalMonitorStateException。
- 与同步 块相比,锁 接口和ReentrantLock 类提供了对并发的更多控制。使用它,一个线程可以在不等待的情况下尝试加锁,消除了死锁 的可能性。
- 我们可以使用其他类,如CountDownLatch、CyclicBarrier、Exchanger、Phaser、SynchronousQueue,以便在多线程应用程序中处理多个线程。
5.总结
在这篇文章中,我们了解了IllegalMonitorStateException, 它的原因是什么以及如何在我们的代码中防止它。我们还看到了一些使用新的并发类的最佳实践,这有助于避免IllegalMonitorStateException。