【面试题】notify() 和 notifyAll()方法的使用和区别
Java中notify和notifyAll的区别
何时在Java中使用notify和notifyAll?
【问】为什么wait()一定要放在循环中?
Java中通知和notifyAll方法的示例
Java中通知和notify方法的示例
Java中notify和notifyAll的区别
Java提供了两个方法notify和notifyAll来唤醒在某些条件下等待的线程,但是Java中的notify和notifyAll之间存在细微差别
当我们使用 notify 时,只有一个等待线程会被唤醒而且它不能保证哪个线程会被唤醒,这取决于线程调度器。关于线程调度的算法如下:
当我们使用 notifyAll 时,那么会唤醒所有线程,这些被唤醒的线程会去争抢锁,谁先抢到谁就先执行,其它的就要继续等待!如何实现?在循环中调用 wait 即可!
因此,notify和notifyAll之间的关键区别在于 notify只会唤醒一个线程,而notifyAll方法将唤醒所有线程。
何时在Java中使用notify和notifyAll?
如果所有线程都在等待相同的条件,并且一次只有一个线程可以从条件变为true,则使用notify 和 notifyAll都可以!
在上述的情况下,notify 的性能是优于 notifyAll,因为 notify 只需要唤醒一个线程,而 notifyAll 会唤醒所有线程,但是最后只会有一个线程继续工作,其它会继续等待,所有可以说被唤醒的大多数线程是“无用”的,这种操作会浪费CPU。
虽然这看起来很合理,但仍有一个警告,即无意中的接收者吞下了关键通知。通过使用notifyAll,我们确保所有收件人都会收到通知。
【问】为什么wait()一定要放在循环中?
永远在 while循环里,而不是 if语句下使用 wait()。这样,循环会在线程睡眠前后都检查 wait的条件,并在条件并未改变的情况下,处理唤醒通知。
正确写法:
synchronized (monitor) {
// 判断条件谓词是否得到满足
while(!locked) {
// 等待唤醒
monitor.wait();
}
// 处理其他的业务逻辑
}
用 if 判断的话,唤醒后线程会从 wait 之后的代码开始运行,但是不会重新判断 if条件,直接继续运行 if 代码块之后的代码;
而如果使用 while 的话,也会从 wait 之后的代码运行,但是唤醒后会重新判断循环条件(重点),如果不成立再执行 while 代码块之后的代码块,成立的话继续 wait!
Java中通知和notifyAll方法的示例
代码描述
public class NotificationTest {
private volatile boolean go = false;
public static void main(String args[]) throws InterruptedException {
NotificationTest test = new NotificationTest();
Runnable waitTask = new Runnable(){
@Override
public void run(){
try {
test.shouldGo();
} catch (InterruptedException ex) {
Logger.getLogger(NotificationTest.class.getName()).
log(Level.SEVERE, null, ex);
}
System.out.println(Thread.currentThread() + " finished Execution");
}
};
Runnable notifyTask = new Runnable(){
@Override
public void run(){
test.go();
System.out.println(Thread.currentThread() + " finished Execution");
}
};
Thread t1 = new Thread(waitTask, "WT1");
Thread t2 = new Thread(waitTask, "WT2");
Thread t3 = new Thread(waitTask, "WT3");
Thread t4 = new Thread(notifyTask,"NT1");
t1.start();
t2.start();
t3.start();
Thread.sleep(200);
t4.start();
}
// waitTask 等待
private synchronized void shouldGo() throws InterruptedException {
while(go != true){
System.out.println(Thread.currentThread()
+ " is going to wait on this object");
wait();
System.out.println(Thread.currentThread() + " is woken up");
}
go = false;
}
// notifyTask 唤醒
private synchronized void go() {
while (go == false){
System.out.println(Thread.currentThread()
+ " is going to notify all or one thread waiting on this object");
go = true;
notifyAll();
}
}
}
运行结果
分析
最初三个线程WT1,WT2,WT3将等待,因为变量go为假,而一个线程NT1将变为真,并通过调用 notifyAll方法通知所有线程,所有线程都将被唤醒
先获得锁的那个线程将会执行结束,并修改go的值为false,这样子另外两个线程(1 和 3)将会继续等待!
Java中通知和notify方法的示例
代码描述
public class NotificationTest {
private volatile boolean go = false;
public static void main(String args[]) throws InterruptedException {
NotificationTest test = new NotificationTest();
Runnable waitTask = new Runnable(){
@Override
public void run(){
try {
test.shouldGo();
} catch (InterruptedException ex) {
Logger.getLogger(NotificationTest.class.getName()).
log(Level.SEVERE, null, ex);
}
System.out.println(Thread.currentThread() + " finished Execution");
}
};
Runnable notifyTask = new Runnable(){
@Override
public void run(){
test.go();
System.out.println(Thread.currentThread() + " finished Execution");
}
};
Thread t1 = new Thread(waitTask, "WT1");
Thread t2 = new Thread(waitTask, "WT2");
Thread t3 = new Thread(waitTask, "WT3");
Thread t4 = new Thread(notifyTask,"NT1");
t1.start();
t2.start();
t3.start();
Thread.sleep(200);
t4.start();
}
private synchronized void shouldGo() throws InterruptedException {
while(go != true){
System.out.println(Thread.currentThread()
+ " is going to wait on this object");
wait();
System.out.println(Thread.currentThread() + " is woken up");
}
go = false;
}
private synchronized void go() {
while (go == false){
System.out.println(Thread.currentThread()
+ " is going to notify or one thread waiting on this object");
go = true;
notify();
}
}
}
运行结果
分析
其实和上面分析的差不多,只不过由于使用的是notify,所以只会唤醒一个线程!