Java多线程设计(二)线程的基本知识(3)线程的协调

1. 概述

在上一节Java多线程设计(二)线程的基本知识(2)共享互斥中,当有一个线程在执行synchronized实例方法时,其它线程就无法执行该方法。这是简单的共享互斥。

假设现在我们想做进一步的处理,例如我现在有一个买烧鸡的店,我想只要店里还有烧鸡,我就让厨师等待着,不要做烧鸡先了,当消费者来买完我的烧鸡,我就通知厨师又开始做烧鸡。

上面的情景可以用Java中的waitnotifynotifyAll三个方法处理。wait方法是让线程乖乖等待,notifynotifyAll方法是唤醒等待中的线程。下面就来详细说一番。

2. wait set——线程的休息室

在开始讲解wait、notify和notifyAll之前,先稍微介绍一下wait set。所有的实例都有一个wait set,wait set是一个在执行该实例的wait方法时,操作停止的线程的集合,它有点像是线程的休息室。

一执行wait方法,线程便会暂时停止操作,进入wait set这个休息室,除非发生下列中的其中一种情况,否则线程会拥有被留在这个wait set里。当发生下列任一情况时,线程便会离开wait set。

  • 有其他线程用notify方法唤醒该线程;
  • 有其他线程以notifyAll方法唤醒该线程;
  • 有其他线程以interrupt方法唤醒该线程;
  • wait方法到期。

3. wait方法——把线程放入wait set

假设一个线程执行了如下的语句:

obj.wait();

则这个线程就会暂时停止执行,进入实例obj的wait set。这个操作称为“线程在obj上wait”。当然了,如果一个线程执行了实例方法中的wait()方法(等同于this.wait())方法,则该线程就会进入this的wait set。此时变成了线程在this上wait。

需要注意的是,wait方法要在同步方法或者同步代码块中执行,不然会抛一个IllegalMonitorStateException异常。

还需要注意的是,wait set只是一个虚拟的概念,不是一个实例的字段,更不是可以获取在实例上wait着的线程列表方法。

下面我画画简单的例子图
(1)获取锁的线程A开始执行wait方法,此时线程B被阻挡着,不能执行synchronized方法

(2)线程A执行完wait方法,进入wait set,并释放锁

(3)线程B获取锁,开始执行synchronized方法

4. notify方法——从wait set拿出线程

使用notify方法时,可以从wait set里抓出1个线程。假设现在执行了下面语句

obj.notify();

则从obj的wait set里的线程中挑1个,唤醒这个线程,被唤醒的这个线程便退出wait set。

(1)继续上面的流程,假设线程B执行的synchronized方法中调用了notify方法,则线程A会被唤醒并且准备继续执行刚刚的wait方法后面的代码,但是注意了,此时线程B还没释放锁,所以线程A并不能马上执行wait方法后面的代码

(2)当线程B执行完synchronized方法后,就会释放锁,此时线程A就可以继续执行wait方法后面的代码了,很简单的一个意思,图我就不画了。

注意了,notify方法和wait方法一样,必须得在同步方法或者同步代码块中执行,不然会抛一个IllegalMonitorStateException异常。

还要注意了,上面的例子也有说到,就是一个线程被唤醒,并不会马上开始执行代码,因为执行notify方法的线程还没释放锁。

那么要是wait set中的线程不止一个呢,notify会唤醒哪一个?这个没有说指定哪个,也没有说怎么选择,姑且就说随机一个吧。

5. notifyAll——从wait set拿出所有线程

notifyAll方法会把所有在wait set中线程唤醒。比如执行下面代码

obj.notifyAll();

则会唤醒实例obj的wait set中所有线程。

注意了,notifyAll方法和notify方法以及wait方法一样,必须得在同步方法或者同步代码块中执行,不然会抛一个IllegalMonitorStateException异常。至于原因,大家可以去这里看看为何wait需要在同步代码块中调用

notify方法和notifyAll方法很类似,就是在唤醒线程的数目上有很大差别,前者是只唤醒一个,或者是唤醒全部。

6. wait、notify、notifyAll是Object类方法

wait、notify、notifyAll方法都是java.lang.Object类的方法,不是Thread类独有的方法。

再来看一下这三个方法的作用:

  • obj.wait()是把当前线程放到obj的wait set中;
  • obj.notify()是从obj的wait set中唤醒一个线程;
  • obj.notifyAll()是唤醒所有在obj的wait set中的线程。

大家可以注意到,这三个方法其实是对obj的wait set进行增、删操作,由于所有的实例都有wait set,所以这三个方法才会是Object类的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值