- 先来看看一段代码:
new Thread(() -> {
synchronized (ReleaseLockDemo.class) {
System.out.printf("线程[%s]进入1号n", Thread.currentThread().getName());
try {
// Thread.sleep(1000);
Thread.currentThread().wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("线程【%s】退出1号n", Thread.currentThread().getName());
}
}).start();
new Thread(() -> {
synchronized (ReleaseLockDemo.class) {
System.out.printf("线程[%s] 进入2号n", Thread.currentThread().getName());
System.out.printf("线程【%s】退出2号n", Thread.currentThread().getName());
}
}).start();
运行结果
- 这段代码的结果是:
线程[Thread-0]进入1号
Exception in thread "Thread-0" 线程[Thread-1] 进入2号
线程【Thread-1】退出2号
java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.concurrency.ReleaseLockDemo.lambda$main$0(ReleaseLockDemo.java:17)
at java.lang.Thread.run(Thread.java:748)
实例疑问
为什么会是这样的结果呢?按道理来说, 应该是下面这个结果才对呀。它为什么会在1号线程等待的时候,2号线程运行了呢,它不应该是要等待1号线线程中的锁释放了才能运行的吗?又为什么会报两个错呢?
线程[Thread-0]进入1号
线程【Thread-0】退出1号
线程[Thread-1] 进入2号
线程【Thread-1】退出2号
那我们先总结下问题:
- 为什么会在1号线程等待的时候,2号线程运行了
- 为什么会报
Exception in thread "Thread-0"错 - 为什么会报
java.lang.IllegalMonitorStateException错
那么带着问题我们来分析下这段代码。更多教程请访问码农之家
实例分析
- 为什么会在1号线程等待的时候,2号线程运行了?
- 就目前的代码而言,我们的锁对象都是
ReleaseLockDemo.class,在两个同步代码块中,用同一个锁,一个代码块运行了,而另一个要运行的话,只有前面的锁释放了后面的代码块才能正常运行。可是在前面的1号线程只运行到了一半就直接运行2号线程了,中间只做了一个wait操作,难道wait操作会引发锁的释放吗? - 这是因为在调用
wait()方法时,monitor被释放了,并且进入休眠状态,然后唤醒其他的线程(可能一个或多个),此时其他线程是可以重新获取到该monitor object的。
- 就目前的代码而言,我们的锁对象都是
- 那么我们来看看这个报错
IllegalMonitorStateException, 这是一个monitor报错- 要想了解这个报错,首先得了解下什么是monitor
- monitor就是监视器,操作系统在面对 进程/线程间同步的时候,所支持的一些同步原语,一般的monitor实现模式是编程语言在语法上提供语法糖,而如何实现monitor机制,则属于编译器的工作,Java就是这么干的。
- monitor的重要特点是,同一时刻,只有一个进程/线程能进入monitor中定义的临界区,这使得monitor能够达到互斥的效果。但仅仅有呼哧的作用是不够的,无法进入monitor临界区的进程/线程,他们应该被阻塞,并且在必要的时候会被唤醒。
- 在Java中的具体实现就是Synchronized;Synchronized关键字在使用时,往往需要指定一个对象与之关联(如:
synchronized(this)),而这个对象就是monitor object。 - 一个线程通过调用某个对象的wait()方法释放该对象的monitor并进入休眠状态,知道其他线程获取来被该线程释放的monitor并调用该对象的notify()或者notifyAll()后再次竞争获取该对象的monitor。这也解释了前面的问题,为什么2号线程会运行。
- 只有拥有该对象monitor的线程才可以调用该对象的notify()和notifyAll()方法;如果没有该对象monitor的线程调用了该对象的notify()或者notifyAll()方法将会抛出
java.lang.IllegalMonitorStateException
- 调用
wait()方法必须要用拥有该对象monitor的线程才可以正常调用,而我们的代码中synchronzed所锁住的对象是ReleaseLockDemo.class。因此,执行该程序后报java.lang.IllegalMonitorStateException错误。
- 要想了解这个报错,首先得了解下什么是monitor
- 至于为什么会报
Exception in thread "Thread-0"错,这个错跟后面的IllegalMonitorStateException是同一个错,只是因为当时线程休眠了,没有执行完而已。
如果换成拥有当前类的对象呢
- 既然要调用
wait()方法那么如果我们把monitor object换成当前类的对象会不会就不会报错了呢?
上代码
public static void main(String[] args) {
WaitMethodTest wmt = new WaitMethodTest();
wmt.run1();
wmt.run2();
}
private void run2() {
new Thread(() -> {
synchronized (this) {
System.out.printf("线程[%s] 进入2号n", Thread.currentThread().getName());
System.out.printf("线程【%s】退出2号n", Thread.currentThread().getName());
}
}).start();
}
private void run1() {
new Thread(() -> {
synchronized (this) {
System.out.printf("线程[%s]进入1号n", Thread.currentThread().getName());
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("线程【%s】退出1号n", Thread.currentThread().getName());
}
}).start();
}
运行结果:
线程Thread-0进入1号
线程Thread-1 进入2号
线程【Thread-1】退出2号
结果分析
- 结果确实没有报错了,但是它好像也没有停止运行
- 这是因为当2号线程运行完成后并没有唤醒1号线程,1号线程还在睡眠状态,所以整个程序没有停止运行。
1299

被折叠的 条评论
为什么被折叠?



