java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台(即 JavaEE(j2ee), JavaME(j2me), JavaSE(j2se))的总称。
在 Object.java 中,定义了 wait(), notify() 和 notifyAll() 等接口。wait() 的作用是让当前线程进入等待状态,同时,wait() 也会让当前线程释放它所持有的锁。下面通过本文给大家介绍 Java 中线程的等待与唤醒知识,感兴趣的朋友一起看看吧
wait(), notify(), notifyAll() 等方法介绍在 Object.java 中,定义了 wait(), notify() 和 notifyAll() 等接口。wait() 的作用是让当前线程进入等待状态,同时,wait() 也会让当前线程释放它所持有的锁。而 notify() 和 notifyAll() 的作用,则是唤醒当前对象上的等待线程;notify() 是唤醒单个线程,而 notifyAll() 是唤醒所有的线程。
Object 类中关于等待 / 唤醒的 API 详细信息如下:
notify() -- 唤醒在此对象监视器上等待的单个线程。
notifyAll() -- 唤醒在此对象监视器上等待的所有线程。
wait() -- 让当前线程处于" 等待 (阻塞) 状态 "," 直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法 ",当前线程被唤醒(进入" 就绪状态 ")。
wait(long timeout) -- 让当前线程处于 "等待 (阻塞) 状态 "," 直到其他线程调用此对象的 notify() 方法或 notifyAll()方法,或者超过指定的时间量 ",当前线程被唤醒(进入" 就绪状态 ")。
wait(long timeout, int nanos) -- 让当前线程处于 "等待 (阻塞) 状态 "," 直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量 ",当前线程被唤醒(进入" 就绪状态 ")。
wait() 和 notify() 示例下面通过示例演示 "wait() 和 notify() 配合使用的情形"。
- // WaitTest.java的源码
- class ThreadA extends Thread {
- public ThreadA(String name) {
- super(name);
- }
- public void run() {
- synchronized(this) {
- System.out.println(Thread.currentThread().getName() + " call notify()");
- // 唤醒当前的wait线程
- notify();
- }
- }
- }
- public class WaitTest {
- public static void main(String[] args) {
- ThreadA t1 = new ThreadA("t1");
- synchronized(t1) {
- try {
- // 启动"线程t1"
- System.out.println(Thread.currentThread().getName() + " start t1");
- t1.start();
- // 主线程等待t1通过notify()唤醒。
- System.out.println(Thread.currentThread().getName() + " wait()");
- t1.wait();
- System.out.println(Thread.currentThread().getName() + " continue");
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
运行结果:
- main start t1
- main wait()
- t1 call notify()
- main continue
结果说明:
如下图,说明了 "主线程" 和 "线程 t1" 的流程。
(01) 注意,图中 "主线程" 代表 "主线程 main"。"线程 t1" 代表 WaitTest 中启动的 "线程 t1"。 而 "锁" 代表 "t1 这个对象的同步锁"。
(02) "主线程" 通过 new ThreadA("t1") 新建 "线程 t1"。随后通过 synchronized(t1) 获取 "t1 对象的同步锁"。然后调用 t1.start() 启动" 线程 t1"。
(03) "主线程" 执行 t1.wait() 释放"t1 对象的锁 "并且进入" 等待 (阻塞) 状态 "。等待 t1 对象上的线程通过 notify() 或 notifyAll()将其唤醒。
(04) "线程 t1" 运行之后,通过 synchronized(this) 获取 "当前对象的锁";接着调用 notify() 唤醒" 当前对象上的等待线程 ",也就是唤醒" 主线程 "。
(05) "线程 t1" 运行完毕之后,释放 "当前对象的锁"。紧接着,"主线程" 获取 "t1 对象的锁",然后接着运行。
对于上面的代码?曾经有个朋友问到过:t1.wait() 应该是让" 线程 t1"等待;但是,为什么却是让" 主线程 main" 等待了呢?
在解答该问题前,我们先看看 jdk 文档中关于 wait 的一段介绍:
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
In other words, this method behaves exactly as if it simply performs the call wait(0).
The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.
注意:jdk 的解释中,说 wait() 的作用是让" 当前线程 "等待,而" 当前线程 " 是指正在 cpu 上运行的线程!
这也意味着,虽然 t1.wait() 是通过" 线程 t1"调用的 wait() 方法,但是调用 t1.wait() 的地方是在" 主线程 main"中。而主线程必须是" 当前线程 ",也就是运行状态,才可以执行 t1.wait()。所以,此时的 "当前线程" 是 "主线程 main"!因此,t1.wait() 是让" 主线程 "等待,而不是" 线程 t1"!
wait(long timeout) 和 notify()wait(long timeout)会让当前线程处于 "等待 (阻塞) 状态 "," 直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量 ",当前线程被唤醒(进入" 就绪状态 ")。
下面的示例就是演示 wait(long timeout) 在超时情况下,线程被唤醒的情况。
- // WaitTimeoutTest.java的源码
- class ThreadA extends Thread {
- public ThreadA(String name) {
- super(name);
- }
- public void run() {
- System.out.println(Thread.currentThread().getName() + " run ");
- // 死循环,不断运行。
- while (true);
- }
- }
- public class WaitTimeoutTest {
- public static void main(String[] args) {
- ThreadA t1 = new ThreadA("t1");
- synchronized(t1) {
- try {
- // 启动"线程t1"
- System.out.println(Thread.currentThread().getName() + " start t1");
- t1.start();
- // 主线程等待t1通过notify()唤醒 或 notifyAll()唤醒,或超过3000ms延时;然后才被唤醒。
- System.out.println(Thread.currentThread().getName() + " call wait ");
- t1.wait(3000);
- System.out.println(Thread.currentThread().getName() + " continue");
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
运行结果:
- main start t1
- main call wait
- t1 run // 大约3秒之后...输出"main continue"
- main continue
结果说明:
如下图,说明了 "主线程" 和 "线程 t1" 的流程。
(01) 注意,图中 "主线程" 代表 WaitTimeoutTest 主线程 (即,线程 main)。"线程 t1" 代表 WaitTest 中启动的线程 t1。 而 "锁" 代表 "t1 这个对象的同步锁"。
(02) 主线程 main 执行 t1.start() 启动" 线程 t1"。
(03) 主线程 main 执行 t1.wait(3000),此时,主线程进入 "阻塞状态"。需要 "用于 t1 对象锁的线程通过 notify() 或者 notifyAll() 将其唤醒" 或者 "超时 3000ms 之后",主线程 main 才进入到 "就绪状态",然后才可以运行。
(04) "线程 t1" 运行之后,进入了死循环,一直不断的运行。
(05) 超时 3000ms 之后,主线程 main 会进入到 "就绪状态",然后接着进入 "运行状态"。
wait() 和 notifyAll()通过前面的示例,我们知道 notify() 可以唤醒在此对象监视器上等待的单个线程。
下面,我们通过示例演示 notifyAll() 的用法;它的作用是唤醒在此对象监视器上等待的所有线程。
- public class NotifyAllTest {
- private static Object obj = new Object();
- public static void main(String[] args) {
- ThreadA t1 = new ThreadA("t1");
- ThreadA t2 = new ThreadA("t2");
- ThreadA t3 = new ThreadA("t3");
- t1.start();
- t2.start();
- t3.start();
- try {
- System.out.println(Thread.currentThread().getName() + " sleep(3000)");
- Thread.sleep(3000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- synchronized(obj) {
- // 主线程等待唤醒。
- System.out.println(Thread.currentThread().getName() + " notifyAll()");
- obj.notifyAll();
- }
- }
- static class ThreadA extends Thread {
- public ThreadA(String name) {
- super(name);
- }
- public void run() {
- synchronized(obj) {
- try {
- // 打印输出结果
- System.out.println(Thread.currentThread().getName() + " wait");
- // 唤醒当前的wait线程
- obj.wait();
- // 打印输出结果
- System.out.println(Thread.currentThread().getName() + " continue");
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
运行结果:
- t1 wait
- main sleep(3000)
- t3 wait
- t2 wait
- main notifyAll()
- t2 continue
- t3 continue
- t1 continue
结果说明:
参考下面的流程图。
(01) 主线程中新建并且启动了 3 个线程 "t1", "t2" 和 "t3"。
(02) 主线程通过 sleep(3000) 休眠 3 秒。在主线程休眠 3 秒的过程中,我们假设 "t1", "t2" 和 "t3" 这 3 个线程都运行了。以 "t1" 为例,当它运行的时候,它会执行 obj.wait() 等待其它线程通过 notify() 或额 nofityAll() 来唤醒它;相同的道理,"t2"和"t3"也会等待其它线程通过 nofity() 或 nofityAll() 来唤醒它们。
(03) 主线程休眠 3 秒之后,接着运行。执行 obj.notifyAll() 唤醒 obj 上的等待线程,即唤醒"t1","t2"和"t3"这 3 个线程。 紧接着,主线程的 synchronized(obj) 运行完毕之后,主线程释放 "obj 锁"。这样,"t1", "t2" 和 "t3" 就可以获取 "obj 锁" 而继续运行了!
为什么 notify(), wait() 等函数定义在 Object 中,而不是 Thread 中Object 中的 wait(), notify() 等函数,和 synchronized 一样,会对 "对象的同步锁" 进行操作。
wait() 会使" 当前线程 "等待,因为线程进入等待状态,所以线程应该释放它锁持有的" 同步锁 ",否则其它线程获取不到该" 同步锁 " 而无法运行!
OK,线程调用 wait() 之后,会释放它锁持有的" 同步锁 ";而且,根据前面的介绍,我们知道:等待线程可以被 notify() 或 notifyAll() 唤醒。现在,请思考一个问题:notify() 是依据什么唤醒等待线程的?或者说,wait() 等待线程和 notify() 之间是通过什么关联起来的?答案是:依据 "对象的同步锁"。
负责唤醒等待线程的那个线程 (我们称为" 唤醒线程 "),它只有在获取" 该对象的同步锁 "(这里的同步锁必须和等待线程的同步锁是同一个),并且调用 notify() 或 notifyAll()方法之后,才能唤醒等待线程。虽然,等待线程被唤醒;但是,它不能立刻执行,因为唤醒线程还持有 "该对象的同步锁"。必须等到唤醒线程释放了 "对象的同步锁" 之后,等待线程才能获取到 "对象的同步锁" 进而继续运行。
总之,notify(), wait() 依赖于 "同步锁",而 "同步锁" 是对象锁持有,并且每个对象有且仅有一个!这就是为什么 notify(), wait() 等函数定义在 Object 类,而不是 Thread 类中的原因。
以上所述是小编给大家介绍的 Java 中线程的等待与唤醒,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 PHPERZ 网站的支持!
来源: http://www.phperz.com/article/18/0102/356816.html