再谈notify和notifyAll的区别和相同

经常在往上逛,关于在java中notify和notifyAll,经常有人有以下的说法:


1

notify只会通知一个在等待的对象,而notifyAll会通知所有在等待的对象,并且所有对象都会继续运行

并且,好像都有例子可以证明。上面的说法,可以说对,也可以说不对。究其原因,在于其中有一点很关键,官方的说法如下所示:


1

2

3

4

5

6

7

8

wait,notify,notifyAll:

此方法只应由作为此对象监视器的所有者的线程来调用。通过以下三种方法之一,线程可以成为此对象监视器的所有者

通过执行此对象的同步实例方法。

通过执行在此对象上进行同步的 synchronized 语句的正文。

对于 Class 类型的对象,可以通过执行该类的同步静态方法。

一次只能有一个线程拥有对象的监视器。

以上说法,摘自javadoc。意思即,在调用中,必须持有对象监视器(即锁),我们可以理解为需要在synchronized方法内运行。那么由此话的隐含意思,即如果要继续由同步块包含的代码块,需要重新获取锁才可以。这句话,在javadoc中这样描述:


01

02

03

04

05

06

07

08

09

10

11

wait

此方法导致当前线程(称之为 T)将其自身放置在对象的等待集中,然后放弃此对象上的所有同步要求。出于线程调度

目的,在发生以下四种情况之一前,线程 T 被禁用,且处于休眠状态:

其他某个线程调用此对象的 notify 方法,并且线程 T 碰巧被任选为被唤醒的线程。

其他某个线程调用此对象的 notifyAll 方法。

其他某个线程中断线程 T。

大约已经到达指定的实际时间。但是,如果 timeout 为零,则不考虑实际时间,在获得通知前该线程将一直等待。

然后,从对象的等待集中删除线程 T,并重新进行线程调度。然后,该线程以常规方式与其他线程竞争,以获得在该对

象上同步的权利;一旦获得对该对象的控制权,该对象上的所有其同步声明都将被恢复到以前的状态,这就是调用 wait

方法时的情况。然后,线程 T 从 wait 方法的调用中返回。所以,从 wait 方法返回时,该对象和线程 T 的同步状态与调

用 wait 方法时的情况完全相同。

即必须重新进行获取锁,这样对于notifyAll来说,虽然所有的线程都被通知了。但是这些线程都会进行竞争,且只会有一个线程成功获取到锁,在这个线程没有执行完毕之前,其他的线程就必须等待了(只是这里不需要再notifyAll通知了,因为已经notifyAll了,只差获取锁了)有如下一个代码,可以重现这个现象。

首先,定义一个可以运行的线程类,如下所示:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

privatestaticfinalObject obj =newObject();

staticclassRimplementsRunnable {

inti;

R(inti) {

this.i = i;

}

publicvoidrun() {

try{

synchronized(obj) {

System.out.println("线程->  "+ i +" 等待中");

obj.wait();

System.out.println("线程->  "+ i +" 在运行了");

Thread.sleep(30000);

}

}catch(Exception e) {

e.printStackTrace();

}

}

}

注意上面的run方法内部,我们在wait()之后,打印一句话,然后将当前代码,暂停30秒。关于sleep方法,是这样描述的:
该线程不丢失任何监视器的所属权。
即仍然持有锁。

然后,定义一个main方法来运行这些线程,如下所示:


01

02

03

04

05

06

07

08

09

10

11

12

Thread[] rs =newThread[10];

for(inti =0;i <10;i++) {

rs[i] =newThread(newR(i));

}

for(Thread r : rs) {

r.start();

}

Thread.sleep(5000);

synchronized(obj) {

obj.notifyAll();

}

我们定义了10个线程,然后全部运行之。因为有wait,10个线程都会在打印出 "开始运行"之后等待。然后main方法调用notifyAll。这里的输出就会出现如下的输出:


01

02

03

04

05

06

07

08

09

10

11

12

线程-> 0 等待中

线程->  4 等待中

线程->  5 等待中

线程->  3 等待中

线程->  2 等待中

线程->  1 等待中

线程->  6 等待中

线程->  7 等待中

线程->  8 等待中

线程->  9 等待中

线程->  9 在运行了

...30秒之内,不会有其他输出

在上面的输出中,在wait之后,只有一个线程输出了"在运行了"语句,并且在一段时间内(这里为30秒),不会有其他输出。即表示,在当前代码持有锁之间,其他线程是不会输出的。

最后结论就是:被wait的线程,想要继续运行的话,它必须满足2个条件:

  1. 由其他线程notify或notifyAll了,并且当前线程被通知到了
  2. 经过和其他线程进行锁竞争,成功获取到锁了

2个条件,缺一不可。其实在实现层面,notify和notifyAll都达到相同的效果,都只会有一个线程继续运行。但notifyAll免去了,线程运行完了通知其他线程的必要,因为已经通知过了。什么时候用notify,什么时候使用notifyAll,这就得看实际的情况了。

时间: 2024-08-30 13:37:00

再谈notify和notifyAll的区别和相同的相关文章

HTTP协议与HTML表单(再谈GET与POST的区别)

HTTP的GET/POST方式有何区别?这是一个老生常谈的问题,但老生常谈的问题往往有一些让人误解的结论.本文将带您浅尝HTTP协议,在了 解HTTP协议的同时将会展示许多被人们忽视的内容.在掌握了HTTP协议的过程中我们将自然而然地了解到GET与POST的本质区别. HTTP请求 从使用者的角度看,一个HTTP请求起始于用户端浏览器上输入的一个URL地址:网页中的一个超链接:提交一个HTML表单.但本质上说,一个HTTP请求起始于用户端向HTTP服务器发送的一个URL请求. 一个标准的HTTP

Notify和NotifyAll的区别?

Notify和NotifyAll都是用来对对象进行状态改变的方式,只是他们的作用域不太一样,从字面上就能看的出来,当对象被上锁之后,当其他的方法要去访问该对象中的数据,就需要该对象对其进行解锁,当然,由于多线程一个时刻可能有很多个方法要去访问他,当采用了Notify就可以实现对指定对象对该对象数据解锁,然而其他对象依旧处于wait状态,当采用NotifyAll时,就实现了对所有对象数据的解锁的作用,所有对象开始竞争资源了.

wait、sleep、notify、notifyAll的区别

threadexceptionobjectjava wait 导致当前的线程等待,直到其他线程调用此对象的 notify方法或 notifyAll 方法.当前的线程必须拥有此对象监视器.该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来.然后该线程将等到重新获得对监视器的所有权后才能继续执行 sleep 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行).该线程不丢失任何监视器的所属权. wait与sl

zbb20180929 thread notify()与notifyAll()的区别

notify(): 唤醒在此对象监视器上等待的单个线程.如果所有线程都在此对象上等待,则会选择唤醒其中一个线程.选择是任意性的,并在对实现做出决定时发生.线程通过调用其中一个 wait 方法,在对象的监视器上等待. 直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程.被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争:例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势. notifyAll(): 唤醒在此对象监视器上等待的所有线程.线程通过调用其中一个 w

notify和notifyAll有什么区别

1 /* 2 * 文件名:NotifyDeadLockDemo.java 3 * 版权:Enmuser Technologies Co.,Ltd. Copyright 2016-2018 4 * 描述:<描述> 5 * 修改人:enmuser 6 * 修改时间:2018年2月24日 7 * 修改单号:<修改单号> 8 * 修改内容:<修改内容> 9 * 10 */ 11 package notify.deadLock; 12 13 /** 14 * <一句话功能描

notify和notifyAll有什么区别?

先说两个概念:锁池和等待池 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中.等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中 在java中,每个对象都有两个池,锁(monitor

java 线程之间通信以及notify与notifyAll区别。

jvm多个线程间的通信是通过 线程的锁.条件语句.以及wait().notify()/notifyAll组成. 下面来实现一个启用多个线程来循环的输出两个不同的语句. package com.app.thread; import javax.swing.plaf.SliderUI;/** * 看出问题来 * @author Gordon * */public class LockDemo { public static void main(String[] args) {//  System.o

Java线程中sleep()、wait()和notify()和notifyAll()、suspend和resume()、yield()、join()、interrupt()的用法和区别

从操作系统的角度讲,os会维护一个ready queue(就绪的线程队列).并且在某一时刻cpu只为ready queue中位于队列头部的线程服务. 但是当前正在被服务的线程可能觉得cpu的服务质量不够好,于是提前退出,这就是yield. 或者当前正在被服务的线程需要睡一会,醒来后继续被服务,这就是sleep. sleep方法不推荐使用,可用wait. 线程退出最好自己实现,在运行状态中一直检验一个状态,如果这个状态为真,就一直运行,如果外界更改了这个状态变量,那么线程就停止运行. sleep(

Java线程中sleep()、wait()和notify()和notifyAll()、yield()、join()等方法的用法和区别

Java线程中sleep().wait()和notify()和notifyAll().suspend和resume().yield().join().interrupt()的用法和区别 从操作系统的角度讲,os会维护一个ready queue(就绪的线程队列).并且在某一时刻cpu只为ready queue中位于队列头部的线程服务. 但是当前正在被服务的线程可能觉得cpu的服务质量不够好,于是提前退出,这就是yield. 或者当前正在被服务的线程需要睡一会,醒来后继续被服务,这就是sleep.