专栏首页程序员小灰漫画:Object类很大,你忍一下(完结篇)

漫画:Object类很大,你忍一下(完结篇)

在上一篇当中,我们提及了Java语言Object类的九大方法,并重点讲解了其中的getClass(),finalize(),toString(),equals(),hashcode()。

没看过的小伙伴,可以点击阅读上一篇:漫画:Object类很大,你忍一下

这一次,我们来重点讲解 wait(),notify(),notifyAll() 这三大方法。

// 执行这个方法后,持有此对象监视器的线程会进入等待队列,同时释放锁
// 如果不在synchronized修饰的方法或代码块里调用,则会抛出IllegalMonitorStateException 异常
// 如果当前线程在等待时被中断,则抛出InterruptedException异常
public final void wait() throws InterruptedException {
    wait(0);
}

// timeout是线程等待时间,时间结束则自动唤醒,单位ms
// Java默认的实现方式,native实现
public final native void wait(long timeout) throws InterruptedException;

// nanos是更精确的线程等待时间,单位ns(1 ms = 1,000,000 ns)
// Java默认的实现方式
public final void wait(long timeout, int nanos) throws InterruptedException {
    if (timeout < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                "nanosecond timeout value out of range");
    }
    if (nanos > 0) {
        timeout++;
    }
    wait(timeout);
}

含参数的wait()方法调用以后,线程可以在等待时间结束后被唤醒;无参的wait()方法调用后,则必须等待持有该对象监视器的线程主动调用notify()或notifyAll()方法后才能被唤醒。

两者之间的区别,在于notify()方法唤醒在此对象监视器上等待的单个线程,而notifyAll()方法则唤醒在此对象监视器上等待的所有线程

与wait方法一样,执行notify方法的线程也必须提前获取锁。需要注意的是,被notify方法唤醒的线程并不会立即执行,因为要等调用notify方法的线程释放锁之后才会获取到锁。

有的Java虚拟机(VM)会选择最先调用wait方法的线程,也有的则会随机选择一个线程。尽管notify方法的处理速度比notifyAll方法更快,但使用notifyAll方法更为稳妥。

notify与notifyAll的方法声明如下:

public final native void notify();
public final native void notifyAll();

有一家公司开始想要招聘程序员,于是面试了几名候选人:

但公司录用程序员的时间是不确定的,需要综合考虑,无法给出及时反馈。于是告诉他们回去等通知。

经过事后的比较和研究,面试官觉得大黄是招聘的最佳人选,于是让HR小姐姐通知大黄,也就是“唤醒”了大黄:

(1)wait()、notify()和notifyAll()必须在synchronized修饰的方法或代码块中使用。

(2)在while循环里而不是if语句下使用wait(),确保在线程睡眠前后都检查wait()触发的条件(防止虚假唤醒)。

(3)wait()方法必须在多线程共享的对象上调用。

生产者/消费者模型能解决绝大多数并发问题,通过平衡生产线程和消费线程的工作能力,来提高程序的整体处理数据的速度。

生产者线程和消费者线程的处理速度差异,会引起消费者想要获取数据时,数据还没生成或者生产者想要交付数据,却没有消费者接收的问题。对于这样的问题,生产者/消费者模型可以消除这个差异。

首先,按照注意事项(1)和(2)的要求,定义一个生产者,往队列里添加元素:

// 生产者,有详细的注释
public class Producer implements Runnable{
    private Queue<Integer> queue;
    private int maxSize;
    public Producer(Queue<Integer> queue, int maxSize){
        this.queue = queue;
        this.maxSize = maxSize;
    }

    @Override
    public void run() {
        // 这里为了方便演示做了一个死循环,现实开发中不要这样搞
        while (true){
            //(1)wait()、notify()和notifyAll()必须在synchronized修饰的方法或代码块中使用
            synchronized (queue){
                //(2)在while循环里而不是if语句下使用wait(),确保在线程睡眠前后都检查wait()触发的条件(防止虚假唤醒)
                while (queue.size() == maxSize){
                    try{
                        System.out.println("Queue is Full");
                        // 生产者线程进入等待状态,在此对象监视器上等待的所有线程(其实只有那个消费者线程)开始争夺锁
                        queue.wait();
                    }catch (InterruptedException ie){
                        ie.printStackTrace();
                    }
                }
                Random random = new Random();
                int i = random.nextInt();
                System.out.println("Produce " + i);
                queue.add(i);
                // 唤醒这个Queue对象的等待池中的所有线程(其实只有那个消费者线程),等待获取对象监视器
                queue.notifyAll();
            }
        }
    }
}

接下来,再定义一个与之类似的消费者类,除了从队列里移除元素的逻辑之外,整体代码大同小异:

// 消费者类
public class Consumer implements Runnable{
    private Queue<Integer> queue;
    private int maxSize;
    public Consumer(Queue<Integer> queue, int maxSize){
        this.queue = queue;
        this.maxSize = maxSize;
    }

    @Override
    public void run() {
        while (true){
            synchronized (queue){
                while (queue.isEmpty()){
                    System.out.println("Queue is Empty");
                    try{
                        queue.wait();
                    }catch (InterruptedException ie){
                        ie.printStackTrace();
                    }
                }
                int v = queue.remove();
                System.out.println("Consume " + v);
                queue.notifyAll();
            }
        }
    }
}

最后编写符合注意事项(3)的测试代码:

public void test(){
    //(3)wait()方法必须在多线程共享的对象上调用
    // 这个队列就是给消费者、生产者两个线程共享的对象
    Queue<Integer> queue = new LinkedList<>();
    int maxSize = 5;
    Producer p = new Producer(queue, maxSize);
    Consumer c = new Consumer(queue, maxSize);
    Thread pT = new Thread(p);
    Thread pC = new Thread(c);
    // 生产者线程启动,获取锁
    pT.start();
    // 消费者线程启动
    pC.start();
}

最终的查看运行结果如下:

Produce 1604006010
Produce 1312202442
Produce -1478853208
Produce 1460408111
Produce 1802825495
Queue is Full
Consume 1604006010
Consume 1312202442
Consume -1478853208
Consume 1460408111
Consume 1802825495
Queue is Empty

本文分享自微信公众号 - 程序员小灰(chengxuyuanxiaohui),作者:东风玖哥,小灰

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-08-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 设计模式之观察者模式

    观察者模式又称为发布-订阅(Publish/Subscribe)模式,是23种设计模式之一。DP中是这么定义观察者模式的:

    端碗吹水
  • 零压力入门算法的顶流畅销书《漫画算法》施展了哪些“魔法”?

    相反,想入门算法,只要选对了书,每一天都可以是情人节! 比如,今天的主角《漫画算法:小灰的算法之旅(Python篇)》,

    博文视点Broadview
  • Python大佬用词云可视化带你分析海贼王、火影和死神三大经典动漫

    对于动漫爱好者来说,海贼王、火影、死神三大动漫神作你肯定肯定不陌生了。小编身边很多的同事仍然深爱着这些经典神作,可见“中毒”至深。今天小编利用P...

    Python进阶者
  • Python大佬用词云可视化带你分析海贼王、火影和死神三大经典动漫

    对于动漫爱好者来说,海贼王、火影、死神三大动漫神作你肯定肯定不陌生了。小编身边很多的同事仍然深爱着这些经典神作,可见“中毒”至深。利用Python大法带大家分析...

    一墨编程学习
  • 7 Papers & Radios | YOLO v4它来了;北航MangaGAN生成久保带人Style漫画形象

    论文 1:Unpaired Photo-to-manga Translation Based on The Methodology of Manga Drawi...

    机器之心
  • QTX看点 | 企鹅巨械QQ MAX BOT横空出世!

    Pat Lee专访 在Marvel或DC漫画改编的电影里,总有类似的场景:一个看似路人般的男子,他们或借助神力、或借助科技,最后拥有强大的力量。 最终惩恶扬...

    腾讯ISUX
  • 漫画 | Bug是如何产生的?

    今天这篇漫画的灵感来源于知乎的万赞回答,也有一部分原因是因为最近工作状态的真实写照吧, 它讲述的是程序员工作中的bug是如何产生的,以及作为一名优秀的程序员,如...

    苏南
  • 火影完结了,而这个消费情怀的小游戏只会「奔跑」

    知晓君
  • Python3网络爬虫(十二):初识Scrapy之再续火影情缘

    版权声明:本文为博主原创文章,未经博主允许不得转载。个人网站:http://cuijiahua.com。 ...

    Jack_Cui
  • 收藏级!史上最全的漫画小程序合辑,让你一次爽到底

    话不多说,知晓程序现在就带大家看看,史前最强掌上漫画小程序合集,地铁公交手机党不容错过的 8 大漫画小程序,让你一次爽到底。

    知晓君
  • 二次元福音,这些触碰名作之壁的ACG都VR化了!

    VRPinea
  • 触漫“蒙眼狂奔”?UGC平台天然就爱不良内容吗?

    快看漫画、触漫等头部漫画企业最近被推上舆论的风口浪尖,不良内容、诱导消费成为被诟病的核心问题,其中一家媒体连续发布多篇文章指出触漫存在不良内容,该文认为触漫是U...

    罗超频道
  • 这才叫积极脱单:为自己搭建相亲网站,一人分饰三十角

    内容提要:霓虹国一直脑洞很大,从动漫到游戏到各种整蛊综艺,总能让我们大跌眼镜,最近有一档节目中就出现了这么一个神奇的程序员小哥......他神奇就神奇在,为自己...

    HyperAI超神经
  • 《邪魔外道》——一个理想主义漫画创作者的自我写照

    近年来,随着漫画产业高速发展,漫画作为一种新的文化符号,正在被越来越多年轻人喜欢并接受。身处于行业中心漫画创作者们,不仅迎来了更多读者,更迎来了读者们更高的期待...

    互联资讯
  • 论游戏创新:好莱坞模式

    技术改变思想 本文为系列文章《论游戏创新》的第三部分,共有5部分,敬请关注。 上一篇为《论游戏创新:新渠道为王》 生产机制 很多时候,游戏的生产和电影的生产非常...

    韩伟
  • 漫画:滑动窗口入门题目,没有之一

    今天是小浩算法“365刷题计划”第83天 。昨天写了一篇感悟,没想到那么受欢迎。几百人转发,好几千人阅读,虚荣心得到了极大的满足。今天继续为大家分享一道经典面试...

    程序员小浩
  • 漫画趣解——队列

    程序员的时光001
  • 几个“高清视频”,就骗了我52块钱???

    小发我纵横 Steam 多年,勉强也可以说是“ 阅游无数 ”了,什么类型的游戏我没玩过?不说别的,光是我一个号的游戏时长就有 5600 小时了。

    小林C语言
  • Swift 三天上手项目,学习经验总结!

    环境 - 基本语法 - 数据类型 - 变量 - 可选项 - 常量 - 字面量 - 运算符 - 条件语句 - 循环 - 字符串 - 字符 - 数组 - 字典 - ...

    iOSSir

扫码关注云+社区

领取腾讯云代金券