java语言中if语句的处理流程(Java中Object类的notify方法会导致死锁吗)
一、notify()和notifyAll()区别
Java提供了两个方法notify和notifyAll来唤醒在某些条件下等待的线程,你可以使用它们中的任何一个,notify() 是对notifyAll()的一个优化,它有很精确的应用场景,并且要求正确使用,不然可能导致死锁。以下为两者区别。
图1 notify()方法源码
图2 notifyAll()方法源码
1、如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
2、notify和notifyAll之间的关键区别在于notify()只会唤醒等待池中的一个线程,进入锁池争夺资源;而notifyAll方法将唤醒对象的等待池中的所有线程,进入锁池。当你调用notify时,只有一个等待线程会被唤醒而且它不能保证哪个线程会被唤醒,这取决于线程调度器。如果你调用notifyAll方法,那么等待该锁的所有线程都会被唤醒,但是在执行剩余的代码之前,所有被唤醒的线程都将争夺对象锁。
3、notify可能会导致死锁,而notifyAll不会导致死锁。
二、为何notify为导致死锁
1、先解释两个概念。a、等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池,等待池中的线程不会去竞争该对象的锁。b、锁池:等待池中的线程,收到notify()或者notifyAll()方法通知的时候,会从等待池中移除,进入到锁池,锁池中的线程会竞争锁资源,只有获取了对象的锁,线程才能执行对象的 synchronized 代码,对象的锁每次只有一个线程可以获得,其他线程只能在锁池中等待,一旦发现对象锁有所释放,便会进入到下一个轮回的锁竞争。
2、解释产生死锁的情况
有上述两个概念就可知,当出现这种情况,多个线程调用了锁对象的wait()方法,这些线程都会进入到等待池中,等待池中的线程不参与锁竞争。此时只调用一次notify()方法,那么只有一个线程会从等待池进入到锁池竞争资源,并且获得锁资源继续执行接下来的代码。执行完毕后,释放锁。但是由于其它线程都处于等待池中,不会去竞争争夺锁,大家都在等待池中等待通知,故而造成了死锁。除非再次调用notify()或者notifyAll()去触发通知,否则会一直等待下去。
三、代码演示notify可能产生死锁
1、执行wait方法,阻塞线程,负责释放当前持有的锁,代码如下:
package org.andy.effective.java.study;
//调用wait方法,阻塞线程,释放当前持有的锁
public class RunnableWait implements Runnable {
private Object obj;
public RunnableWait(Object obj) {
this.obj = obj;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() "【run on RunnableWait】");
// 同步代码块
synchronized (obj) {
System.out.println(Thread.currentThread().getName() "【obj to wait on RunnableWait】");
try {
// 释放锁
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() "【obj continue to run on RunnableWait】");
}
}
}
2、持有锁的代码块执行完毕,调用notify方法,释放锁,仅通知等待池中一个线程进入锁池竞争获取锁,代码如下:
3、持有锁的代码块执行完毕,调用notifyAll方法,释放锁,通知等待池中所有线程进入锁池竞争获取锁,代码如下:
package org.andy.effective.java.study;
//持有锁的代码块执行完毕,调用notifyAll方法,释放锁,通知等待池中所有线程进入锁池竞争获取锁
public class RunnableNotifyAll implements Runnable {
private Object obj;
public RunnableNotifyAll(Object obj) {
this.obj = obj;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() "【run on RunnableNotifyAll】");
System.out.println("睡眠3秒...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 同步代码块
synchronized (obj) {
System.out.println(Thread.currentThread().getName() "【notify obj on RunnableNotifyAll】");
// 通知所有等待线程
obj.notifyAll();
}
}
}
4、wait和notify配合产生死锁代码样例:
图3 notify方法可能导致死锁样例
5、wait和notifyAll配合使用,不会导致死锁。
图4 notifyAll方法不会产生死锁
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com