当前位置: 首页 > news >正文

java 手写阻塞队列_「面试」Java进阶 用wait和notify实现一个阻塞队列

以下内容均由本人独立完成,希望你看完之后能有更多更深入的了解,欢迎关注➕

如下图就是一个简单都阻塞队列都模型,线程1往里面塞数据,线程2从中取数据,接下来就利用Java中最基础的wait和notify实现一个阻塞队列。

d7f8461dd375f040f5cf1c243c8208c7.png

本图来自:http://tutorials.jenkov.com/java-concurrency/blocking-queues.html

wait和notify以及notifyAll 都是Object类提供的基本native本地方法,可提供多线程间协作功能,可以控制线程的暂停和唤醒等操作,也就可以实现简洁版本的消费者生产者模型。接下来就学习下其使用特点,最后利用wait和notify编写一个生产者消费者模型的队列。

wait和notify的特点

  • wait() 、notify() 使用前需要获取当前对象监视器Monitor对象,一般情况和synchronized关键字配合使用
  • wait() 方法调用后会使得当前所在的线程暂停运行,并且进入到Monitor监视器的Wait Set集合中,具体可看如下图
023f53f898fd6133deabb866ee47511b.png
  • notify() 方法是可以随机唤醒一个在WaitSet集合内的线程进入到EntryList(图中的EntrySet)中,这会导致死锁,例如如下代码块
private boolean flag = true;public synchronized void deal() { try { while(!flag) { this.wait(); // 这个this就是指当期对象,同时synchronized也是同步方法,指定的监视器是同一个 // 而且需要注意这个while 循环 } flag = false; this.notify(); // 理由同上 System.out.println("唤醒了一个线程") } catch (Execption e) { e.printStackTrace(); }}

现在假设有线程A、B、C,其中AB都已经经过wait进入到了等待状态,现在线程C中执行了notify()之后,线程A被唤醒,可是这个时候flag值已经被线程C改成了false,那么while就是继续成立的,那么就使得刚刚被唤醒的线程A很快再次执行wait() 进入到等待状态,使得所有的线程全部进入等待,没有线程可以运行,出现死锁状态

这点也是使用wait和notify的缺点,无法指定类型的线程唤醒操作。如果不注意的情况下使用了notify(),就会因为唤醒了不合适的线程出现死锁

  • notifyAll() 则是可以唤醒所有wait的线程,需要注意的是唤醒了并不意味着执行了,唤醒这个操作只是相当于所有的WaitSet集合的线程进入到EntryList中,随后还是需要通过竞争CPU时间片获取才可以真正的继续执行线程,这就存在一个问题了,如果恰好最后执行的线程又是不合适的线程,同样导致死锁。有人会说可以调低线程的优先级,可是这更不好控制线程工作,反而会出现饥饿的情况。所以notifyAll() 依旧可能会出现死锁,只是概率降低了,在一般场景下如果可以预见线程的情况,优先建议使用notifyAll()
  • wait以及notify的暂停和唤醒是没有先后顺序的,也就使得在开发中必须保证wait在notify之前被调用

生产者消费者模型实践

生产者消费者模型就是当队列为空时消费者停止消费,当队列满时生产者停止生产。利用wait 和 notify 协调生产者线程和消费者线程的关系,再加上一个数组作为队列的容器,生产者的偏移量以及消费者的偏移量就可以完成一个简单的消费者生产者模型,阻塞队列原理也基本类似。具体如下代码

public class WaitAndNotifyBlockQueue { private Object OBJ = new Object(); private Object[] items; private int count = 0; private int productIndex = 0; private int consumerIndex = 0; public WaitAndNotifyBlockQueue(int count) { this.items = new Object[count]; printf(); } public void put(E e) { synchronized (OBJ) { try { while (count >= items.length) { OBJ.wait(); } if (productIndex+1 > items.length) { productIndex = 0; } items[productIndex++] = e; count += 1; System.out.println(Thread.currentThread().getName() + " product:" + e); printf(); OBJ.notify(); } catch (InterruptedException e1) { e1.printStackTrace(); } } } public E get() { synchronized (OBJ) { try { while (count <=0) { OBJ.wait(); } if (consumerIndex+1 > items.length) { consumerIndex = 0; } E e = (E) items[consumerIndex++]; count -= 1; System.out.println(Thread.currentThread().getName() + " consumer:" + e); printf(); OBJ.notify(); return e; } catch (InterruptedException e1) { e1.printStackTrace(); } } return null; } public int getCount() { synchronized (OBJ) { return count; } } private void printLine() { System.out.println(); for (int i = 0; i < items.length; i++) { System.out.print("-----"); } System.out.println(); } // 便于查看数据而已,无实际用途 public void printf() { synchronized (OBJ) { for (int i = 1; i <= items.length; i++) { if (i == productIndex) { System.out.print(" ⬇ "); } else { System.out.print(" "); } } printLine(); for (int i = 0; i < items.length; i++) { Object num = items[i]; if (num != null) { System.out.printf("%4d

相关文章:

  • arcgis 字段计算器 条件赋值_ArcGIS 10.2字段计算器(Field Calculator)批量条件赋值用法总结...
  • 10a大电流稳压芯片_精密稳压芯片TL431在电子电路中有什么作用?来了解一下吧...
  • 荧光皮肤有哪些_如何让皮肤白的发光?
  • vue输出语句_图解 VueLoader : .vue 文件是如何被打包的?
  • cifs挂载 mount ubuntu_ubuntu16.04挂载根文件系统报错mount:RPC:Unable to send;errno=Network is unreachable...
  • 华为手机输入键盘声音_华为手机默认输入法有6种技巧,炫酷加实用,网友:这谁顶得住...
  • 与context的关系_在React中使用Context的两点注意事项
  • cordova云相册插件_ionic 中使用 cordova camera 插件选择本地图片显示问题 -问答-阿里云开发者社区-阿里云...
  • 判断按键值_TinyUI(嵌入式UI库)-按键移植
  • 布局pd_PD-1/L1之后,肿瘤免疫疗法的这些方向也可能成为爆款!
  • 怎么投屏_手机投屏竖屏显示器怎么全屏
  • c++ map作为返回值_详解 C++ STL 中 map::erase 的正确姿势
  • python试卷(有答案版本、个人答案不是官方答案)_python试卷(有答案版本,个人答案不是官方答案)(精品文档)_共7页...
  • echarts 饼图进度条_Echarts实现环状半圆形饼图
  • 多开脚本_现阶段魔兽世界怀旧服晚上脚本成灾?G币会暴跌吗?
  • 自己简单写的 事件订阅机制
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • 【知识碎片】第三方登录弹窗效果
  • Android 架构优化~MVP 架构改造
  • cookie和session
  • Java IO学习笔记一
  • Laravel核心解读--Facades
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • PHP CLI应用的调试原理
  • socket.io+express实现聊天室的思考(三)
  • Theano - 导数
  • uva 10370 Above Average
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • Vue 2.3、2.4 知识点小结
  • 彻底搞懂浏览器Event-loop
  • 对象管理器(defineProperty)学习笔记
  • 将 Measurements 和 Units 应用到物理学
  • 警报:线上事故之CountDownLatch的威力
  • 目录与文件属性:编写ls
  • 前端知识点整理(待续)
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 入门级的git使用指北
  • 小程序01:wepy框架整合iview webapp UI
  • 一些css基础学习笔记
  • 异步
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (二)springcloud实战之config配置中心
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (附源码)计算机毕业设计ssm高校《大学语文》课程作业在线管理系统
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (三十五)大数据实战——Superset可视化平台搭建
  • (十八)SpringBoot之发送QQ邮件
  • (四)Android布局类型(线性布局LinearLayout)
  • (一)pytest自动化测试框架之生成测试报告(mac系统)