Java中notify()和notifyAll()的区别
notify()和notifyAll()方法以及wait()方法用于线程之间的通信。通过调用wait()方法进入等待状态的线程将一直处于等待状态,直到任何其他线程调用同一对象上的notify()或notifyAll()方法。
notify(): notify()方法在Object类中定义,该类是Java的顶级类。它只用于唤醒一个正在等待对象的线程,然后该线程开始执行。线程类notify()方法用于唤醒单个线程。
notifyAll(): notifyAll()唤醒正在等待该对象监视器上的所有线程。线程通过调用一个等待方法来等待对象的监视器。在当前线程放弃该对象上的锁之前,被唤醒的线程将无法继续运行。
现在的问题是notify()和notifyAll()方法都是用来给等待的线程发送通知的,那么它们之间有什么区别,或者我们应该在哪里使用notify()方法和我们应该去哪里使用notifyAll()方法?
Sr. No. | Key | notify() | notifyAll() |
---|---|---|---|
1 | 通知 | 在多线程的情况下,notify()方法只向等待发送锁的多个等待线程中的一个线程发送通知。 | 而同一上下文中的notifyAll()方法会向所有等待的线程发送通知,而不是单个线程。 |
2 | 线程标识 | 在notify()方法的情况下,通知被发送到多个等待线程中的单个线程,因此可以确定哪些等待线程将接收锁。 | 另一方面,notifyAll()向所有等待的线程发送通知。因此,不清楚哪个线程将接收锁。 |
3 | 风险因素 | 在notify()方法的情况下,线程丢失的风险很高,因为通知只发送给单个线程,如果它丢失了该通知,那么其他线程将得不到通知,从而得不到锁。 | 而在notifyAll()的情况下,它会向所有等待的线程发送通知,因此,如果任何线程错过了通知,就会有其他线程来完成这项工作。因此风险较小。 |
4 | 性能 | 与notifyAll()方法相比,notify()方法的内存和CPU消耗更少,因为通知是发送给单个线程的,所以与notifyAll()方法相比,性能更好。 | 另一方面,不删除通知的代价是将通知发送给所有等待的线程,与notify()相比,内存和CPU消耗更多,因此notifyAll()的性能更低。 |
5 | 可互换的 | 在notify()方法的情况下,图像中只有一个线程,因此没有线程可互换的概念是可能的。 | 如果你的所有等待线程都是可交换的(它们被唤醒的顺序无关紧要),我们应该使用notifyAll()。 |
让我们来理解notify()方法的行为:
// Java program to illustrate the
// behaviour of notify() method
class Geek1 extends Thread {
public void run()
{
synchronized (this)
{
System.out.println(
Thread.currentThread().getName()
+ "...starts");
try {
this.wait();
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
Thread.currentThread().getName()
+ "...notified");
}
}
}
class Geek2 extends Thread {
Geek1 geeks1;
Geek2(Geek1 geeks1){
this.geeks1 = geeks1;
}
public void run()
{
synchronized (this.geeks1)
{
System.out.println(
Thread.currentThread().getName()
+ "...starts");
try {
this.geeks1.wait();
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
Thread.currentThread().getName()
+ "...notified");
}
}
}
class Geek3 extends Thread {
Geek1 geeks1;
Geek3(Geek1 geeks1) { this.geeks1 = geeks1; }
public void run()
{
synchronized (this.geeks1)
{
System.out.println(
Thread.currentThread().getName()
+ "...starts");
this.geeks1.notify();
System.out.println(
Thread.currentThread().getName()
+ "...notified");
}
}
}
class MainClass {
public static void main(String[] args)
throws InterruptedException
{
Geek1 geeks1 = new Geek1();
Geek2 geeks2 = new Geek2(geeks1);
Geek3 geeks3 = new Geek3(geeks1);
Thread t1 = new Thread(geeks1, "Thread-1");
Thread t2 = new Thread(geeks2, "Thread-2");
Thread t3 = new Thread(geeks3, "Thread-3");
t1.start();
t2.start();
Thread.sleep(100);
t3.start();
}
}
输出:
Thread-1...start
Thread-2...starts
Thread-3...starts
Thread-3...notified
Thread-1...notified
让我们来理解notifyAll()方法的行为:
// Java program to illustrate the
// behavior of notifyAll() method
class Geek1 extends Thread {
public void run()
{
synchronized (this)
{
System.out.println(
Thread.currentThread().getName()
+ "...starts");
try {
this.wait();
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
Thread.currentThread().getName()
+ "...notified");
}
}
}
class Geek2 extends Thread {
Geek1 geeks1;
Geek2(Geek1 geeks1){
this.geeks1 = geeks1;
}
public void run()
{
synchronized (this.geeks1)
{
System.out.println(
Thread.currentThread().getName()
+ "...starts");
try {
this.geeks1.wait();
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
Thread.currentThread().getName()
+ "...notified");
}
}
}
class Geek3 extends Thread {
Geek1 geeks1;
Geek3(Geek1 geeks1) { this.geeks1 = geeks1; }
public void run()
{
synchronized (this.geeks1)
{
System.out.println(
Thread.currentThread().getName()
+ "...starts");
this.geeks1.notifyAll();
System.out.println(
Thread.currentThread().getName()
+ "...notified");
}
}
}
class MainClass {
public static void main(String[] args)
throws InterruptedException
{
Geek1 geeks1 = new Geek1();
Geek2 geeks2 = new Geek2(geeks1);
Geek3 geeks3 = new Geek3(geeks1);
Thread t1 = new Thread(geeks1, "Thread-1");
Thread t2 = new Thread(geeks2, "Thread-2");
Thread t3 = new Thread(geeks3, "Thread-3");
t1.start();
t2.start();
Thread.sleep(100);
t3.start();
}
}
输出:
Thread-1...starts
Thread-2...starts
Thread-3...starts
Thread-3...notified
Thread-1...notified
Thread-2...notified
什么时候使用notify()方法和notifyAll()
- 在互斥锁的情况下,只有一个等待线程在收到通知后可以做一些有用的事情(在这种情况下获得锁)。在这种情况下,您宁愿使用notify()。如果实现正确,您也可以在这种情况下使用notifyAll(),但是您将不必要地唤醒那些无论如何都不能做任何事情的线程。
- 在某些情况下,一旦等待结束,所有等待线程都可以采取有用的操作。例如,一组等待某个任务完成的线程;一旦任务完成,所有等待线程就可以继续它们的业务。在这种情况下,您可以使用notifyAll()同时唤醒所有正在等待的线程。
notify()和notifyAll()的应用
- 对共享资源的维护操作,其中多个线程在访问资源之前等待操作完成;对于这些,我们应该使用notifyAll()。
- 假设我们有一个生产者线程和一个消费者线程。生产者生产的每一个“包”都应该被消费者消费。消费者在队列中放入一些东西,然后调用notify()。
- 当一个漫长的过程结束时,我们希望有一个通知。你想要哔哔声和屏幕更新。进程执行notifyAll()通知提示线程和屏幕更新线程。