JUC并发编程 -- 为什么需要wait/notify方法 & 原理之 wait / notify & wait() 和 notify() API介绍

Posted Z && Y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC并发编程 -- 为什么需要wait/notify方法 & 原理之 wait / notify & wait() 和 notify() API介绍相关的知识,希望对你有一定的参考价值。

1. 为什么需要wait/notify方法

  • 由于条件不满足,小南不能继续进行计算
  • 但小南如果一直占用着锁,其它人就得一直阻塞,效率太低
  • 于是老王单开了一间休息室(调用 wait 方法),让小南到休息室(WaitSet)等着去了,但这时锁释放开,其它人可以由老王随机安排进屋
  • 直到小M将烟送来,大叫一声 [ 你的烟到了 ] (调用 notify 方法)
  • 小南于是可以离开休息室,重新进入竞争锁的队列

2. 原理之 wait / notify

  • Owner 线程发现条件不满足,调用 wait 方法,即可进入 WaitSet 变为 WAITING / TIMED_WAITING 状态(已经获得了锁,但是条件不满足又释放了锁
  • BLOCKED(没有获得锁 正在等待获取锁) 和 WAITING / TIMED_WAITING 的线程都处于阻塞状态,不占用 CPU 时间片,BLOCKED 线程会在 Owner 线程释放锁时唤醒
  • WAITING / TIMED_WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入EntryList 重新竞争

3. notify & wait() 和 notify() API介绍


1.1 API介绍

它们都是线程之间进行协作的手段,都属于 Object 对象的方法。必须获得此对象的锁,才能调用这几个方法

  • obj.wait() 让进入 object 监视器的线程到 waitSet 等待
  • obj.notify() 在 object 上正在 waitSet 等待的线程中挑一个唤醒
  • obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒

1.2 示例代码:

notify(): 唤醒随机一个等待状态的线程

package tian;

import lombok.extern.slf4j.Slf4j;


@Slf4j(topic = "c.TestWaitNotify")
public class TestWaitNotify {
    final static Object obj = new Object();

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            synchronized (obj) {
                log.debug("执行....");
                try {
                    obj.wait(); // 让线程在obj上一直等待下去
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("t1线程的其它代码....");
            }
        }, "t1").start();

        new Thread(() -> {
            synchronized (obj) {
                log.debug("执行....");
                try {
                    obj.wait(); // 让线程在obj上一直等待下去
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("t2线程的其它代码....");
            }
        }, "t2").start();

        // 主线程两秒后执行
        Thread.sleep(2000);
        log.debug("唤醒 obj 上其它线程");
        synchronized (obj) {
            obj.notify(); // 唤醒obj上随机一个等待状态的线程
//            obj.notifyAll(); // 唤醒obj上所有等待线程
        }
    }
}

运行结果:

这里唤醒了t1线程

notifyAll(): 唤醒所有等待线程

上诉代码改进:

运行结果:


1.3 带参数的wait()方法

  • wait() 方法会释放对象的锁,进入 WaitSet 等待区,从而让其他线程就机会获取对象的锁。无限制等待,直到notify 为止

  • wait(long n) 有时限的等待, 到 n 毫秒后结束等待继续运行线程后面的代码,或是被 notify 后运行线程后面的代码。

  • wait(long timeout, int nanos) 这个方法里面有2个参数第一个参数是毫秒,第二个参数是纳秒,但这个纳秒是个假的纳秒,所以用的很少,一般用带一个参数的wait方法就足够了。



以上是关于JUC并发编程 -- 为什么需要wait/notify方法 & 原理之 wait / notify & wait() 和 notify() API介绍的主要内容,如果未能解决你的问题,请参考以下文章

JUC并发编程 -- 为什么需要wait/notify方法 & 原理之 wait / notify & wait() 和 notify() API介绍

JUC 高并发编程

JUC 高并发编程

多线程进阶=>JUC并发编程

好课分享: JUC并发编程与源码分析 百度云

JUC并发编程线程池及相关面试题 详解