1. 概述
在上一节中,当有一个线程在执行 synchronized 实例方法时,其它线程就无法执行该方法。这是简单的共享互斥。
假设现在我们想做进一步的处理,例如我现在有一个买烧鸡的店,我想只要店里还有烧鸡,我就让厨师等待着,不要做烧鸡先了,当消费者来买完我的烧鸡,我就通知厨师又开始做烧鸡。
上面的情景可以用 Java 中的 wait、notify 和 notifyAll 三个方法处理。wait 方法是让线程乖乖等待,notify 和 notifyAll 方法是唤醒等待中的线程。下面就来详细说一番。
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 异常。
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 类的方法。
就爱阅读 www.92to.com 网友整理上传, 为您提供最全的知识大全, 期待您的分享,转载请注明出处。
来源: