服务器之家

服务器之家 > 正文

Java notify和notifyAll的区别和相同

时间:2020-06-15 12:02     来源/作者:姚国俊

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

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

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

wait,notify,notifyAll:

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

通过执行此对象的同步实例方法。
通过执行在此对象上进行同步的 synchronized 语句的正文。
对于 Class 类型的对象,可以通过执行该类的同步静态方法。 
 一次只能有一个线程拥有对象的监视器。

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

wait

此方法导致当前线程(称之为 T)将其自身放置在对象的等待集中,然后放弃此对象上的所有同步要求。出于线程调度
目的,在发生以下四种情况之一前,线程 T 被禁用,且处于休眠状态:
其他某个线程调用此对象的 notify 方法,并且线程 T 碰巧被任选为被唤醒的线程。
其他某个线程调用此对象的 notifyAll 方法。
其他某个线程中断线程 T。
大约已经到达指定的实际时间。但是,如果 timeout 为零,则不考虑实际时间,在获得通知前该线程将一直等待。
 然后,从对象的等待集中删除线程 T,并重新进行线程调度。然后,该线程以常规方式与其他线程竞争,以获得在该对
象上同步的权利;一旦获得对该对象的控制权,该对象上的所有其同步声明都将被恢复到以前的状态,这就是调用 wait
 方法时的情况。然后,线程 T 从 wait 方法的调用中返回。所以,从 wait 方法返回时,该对象和线程 T 的同步状态与调
用 wait 方法时的情况完全相同。

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static final Object obj = new Object();
  static class R implements Runnable {
    int i;
 
    R(int i) {
      this.i = i;
    }
 
    public void run() {
      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方法来运行这些线程,如下所示:

?
1
2
3
4
5
6
7
8
9
10
11
12
Thread[] rs = new Thread[10];
    for(int i = 0;i < 10;i++) {
      rs[i] = new Thread(new R(i));
    }
    for(Thread r : rs) {
      r.start();
    }
 
    Thread.sleep(5000);
    synchronized(obj) {
      obj.notifyAll();
    }

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

线程-> 0 等待中
线程->  4 等待中
线程->  5 等待中
线程->  3 等待中
线程->  2 等待中
线程->  1 等待中
线程->  6 等待中
线程->  7 等待中
线程->  8 等待中
线程->  9 等待中
线程->  9 在运行了
...30秒之内,不会有其他输出

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

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

由其他线程notify或notifyAll了,并且当前线程被通知到了

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

以上就是对Java notify和NotifyAll的资料整理,后续继续补充相关资料谢谢大家对本站的支持!

标签:

相关文章

热门资讯

歪歪漫画vip账号共享2020_yy漫画免费账号密码共享
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享 2020-04-07
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意
沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意 2019-07-07
玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分
玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分 2019-06-21
男生常说24816是什么意思?女生说13579是什么意思?
男生常说24816是什么意思?女生说13579是什么意思? 2019-09-17
返回顶部

448
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40