【重难点】【JUC 03】怎么实现一个线程安全的队列、手写模拟实现一个阻塞队列

文章目录

  • 【重难点】【JUC 03】怎么实现一个线程安全的队列、手写模拟实现一个阻塞队列
  • 一、怎么实现一个线程安全的队列
    • 1.阻塞队列
    • 2.非阻塞队列
    • 3.总结
  • 二、手写模拟实现一个阻塞队列
    • 1.使用 synchronized 实现
    • 2.使用 ReentrantLock

一、怎么实现一个线程安全的队列

Java 提供的线程安全的队列可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是 BlockingQueue,非阻塞队列的典型例子是 ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列

1.阻塞队列

顾名思义,可以提供阻塞功能的队列

阻塞队列提供的常用方法:

可能报异常 可能阻塞 返回布尔值 设定等待时间
入队 add(e) offer(e) put(e) offer(e, timeout, unit )
出队 remove() poll() take() poll(timeout, unit)
查看 element() peek()
  • add、remove、element 方法不会阻塞线程,当不满足约束条件时,会抛出 IllegalStateException 异常。例如:当队列满时调用 add
  • offer、poll、peek 方法既不会阻塞线程,也不会抛出异常。例:当队列满时调用 offer,则不会插入元素,会返回 false
  • 要想实现阻塞功能,就要调用 put 和 take 方法

ArrayBlockingQueue

基于数组的阻塞队列,在 ArrayBlocking 内部,维护了一个定长数组,以便缓存队列中的数据对象。此外,内部还保存着两个整形变量,分别标识着队列的头部和尾部在数组中的位置

ArrayBlockingQueue 在生产者放入数据和消费者获取数据时,用的是同一个锁对象,这也意味着两者无法真正并行运行,这一点尤其不同于 LinkedBlockingQueue。按照实现原理来分析,ArrayBlockingQueue 完全可以采用分离锁,从而实现生产者和消费者完全并行运行。之所以不这么做是因为 ArrayBlockingQueue 的数据写入和获取已经足够轻巧,以至于引入分离锁,除了给代码带来不必要的复杂性外,在性能上没有任何优势。ArrayBlockingQueue 和 LinkedBlockingQueue 间还有一个明显的不同之处在于:前者在插入或删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的 Node 对象。这在长时间内需要高效并发地处理大批量数据的系统中,其对于 GC 还是有一定的影响

LinkedBlockingQueue

基于链表的阻塞队列,与 ArrayListBlockingQueue 类似,其内部也维护了一个数据缓冲队列,区别在于这个队列由链表构成。当生产者向队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回。只有当队列缓冲区达到最大值缓存容量时(LinkedBlcokingQueue 可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,对于消费者的处理也是如此。LinkedBlockingQueue 之所以能够高效地处理并发数据,还有一个原因,其对于生产者和消费者采用了分离锁来控制数据同步。需要注意的是,如果创建了一个 LinkedBlockingQueue 对象而没有指定容量,LinkedBlockingQueue 会默认设置为 Integer.MAX_VALUE。这样就会带来一个问题,如果生产者的生产速度远大于消费者的消费速度,可能会导致 OOM

2.非阻塞队列

当许多线程共享访问一个公共集合时,ConcurrentLinkedQueue 是一个恰当的选择,此队列不允许 null 元素

3.总结

在并发编程中,一般推荐使用阻塞队列,这样可以尽量避免程序出现意外的错误。阻塞队列最经典的应用场景就是 socket 客户端数据的读取和解析,读取数据的线程不断地将数据放入队列,然后解析线程不断地从队列获取数据解析

使用非阻塞队列虽然可以即使返回结果或者消费结果,但是必须自行编码解决返回为空的情况和消费重试等问题

它们都是线程安全的,不用考虑线程同步问题

二、手写模拟实现一个阻塞队列

1.使用 synchronized 实现

由于 synchronized 是同一把锁,所以使用 notify() 可能会唤醒非目标线程,notifyAll() 唤醒全部线程则会带来大量的 CPU 上下文交换和锁竞争

public class ArrayBlockingQueue{private Object[] array;      //数组private int head;           //头private int tail;            //尾private volatile int size;   //元素个数public ArrayBlockingQueue(int capacity){this.array = new Object[capacity];}//写入元素public synchronized void put(Object o) throws InterruptedException{//当队列满时,阻塞while(size == array.length){this.awit();}array[tail++] = o;if(tail ==array.length){tail = 0;}size++;//唤醒线程this.notifyAll();}//取出元素public synchronized Object get() throws InterruptedException{//当队列为空,阻塞while(size == 0){this.wait();}Object o = array[head++];if(head == array.length){head = 0;}size--;//唤醒线程this.notifyAll();return o;}
}

2.使用 ReentrantLock

可以使用 Condition 指定要唤醒的线程,所以效率高

public class ArrayBlockingQueue{private Object[] array;      //数组private int head;           //头private int tail;            //尾private volatile int size;   //元素个数private ReentrantLock lock = new ReentrantLock();    //锁private Condition notEnpty = lock.newCondition();   //非空private Condition notFull = lock.newCondition();   //非满public ArrayBlockingQueue(int capacity){this.array = new Object[capacity];}//写入元素public void put(Object o) throws InterruptedException{try{lock.lock();//当队列满时,阻塞while(size == array.length){notFull.wait();}array[tail++] = o;if(tail == array.length){tail = 0;}size++;//唤醒线程notEmpty.notifyAll();}finally{lock.unlock();}}//取出元素public Object get() throws InterruptedException{lock.lock();try{//当队列为空,阻塞while(size == 0){notEmpty.wait();}Object o = array[head++];if(head == array.length){head = 0;}size--;//唤醒线程notFull.notyfyAll();return o;}finally{lock.unlock();}}
}

【重难点】【JUC 03】怎么实现一个线程安全的队列、手写模拟实现一个阻塞队列相关推荐

  1. 详细的线程池讲解,手写C与C++版本

    详细的线程池讲解,手写C与C++版本 在此感谢苏丙榅的教程讲的很详细,我看了他的C版本教程,对线程池有了深刻理解,手写了C版本,并自主改了C++版本. 线程池是消费者生产者模型的其中之一.这里面的线程 ...

  2. 计算机视觉与深度学习 | 基于MATLAB 使用CNN拟合一个回归模型来预测手写数字的旋转角度(卷积神经网络)

    博主github:https://github.com/MichaelBeechan 博主CSDN:https://blog.csdn.net/u011344545 上一篇写了一个:实现简单的数字分类 ...

  3. python手写多个字母识别_一个带界面的CNN手写数字识别,使用Python(tensorflow, kivy)实现...

    CNN_Handwritten_Digit_Recognizer (CNN手写数字识别) A CNN handwritten digit recognizer with graphical UI, i ...

  4. jmeter一个线程组多个请求_Jmeter模拟真实用户压测场景之阶梯螺纹线程组、终极线程组、并发线程组实例...

    我们有时需要模拟非常真实复杂的用户压测场景,可以用到此插件来设计场景 1.安装插件,选项--Plugins Manager打开安装页面 2.搜索standard set并安装,重启jmeter,查看测 ...

  5. 深读源码-java线程系列之自己手写一个线程池

    问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写的线程池如何测试? 简介 线程池是Java并发编程中经常使用到的技术,那么自己如何动手写一个线程池呢?本文将手把手带你写一个可用的线 ...

  6. python是如何实现进程池和线程池的_高并发:线程、线程锁与线程池(精华),手写代码实现线程池...

    前文: 单线程--多线程的开启--线程锁--线程同步工具--手写连接池--连接池工具类. 一.线程 1.线程的概念 2.线程与进程的关系 3.定义: 区别:如上!!! 4.wait()和sleep() ...

  7. c++socket多个客户端通过不同端口与一个服务端通信_手写RPC,深入底层理解整个RPC通信...

    一.前言 RPC,远程过程调用,调用远程方法像调用本地方法一样.RPC交互分为客户端和服务端,客户端调用服务端方法,服务端接收数据并打印到控制台,并response响应给客户端. RPC和HTTP的联 ...

  8. JAVA项目代码手写吗_一个老程序员是如何手写Spring MVC的

    见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十多 ...

  9. 方法 手写promise_实现一个符合 Promise/A+规范的 Promise(typescript 版)

    (给前端大全加星标,提升前端技能) 转自:Col0ring juejin.cn/post/6886360224308035598 写在前面 没错,这又是一篇关于手写 Promise 的文章,想必大家已 ...

最新文章

  1. 国外优秀开源PHP建站程序一览
  2. Java_IO流_抽象类
  3. mysql系列十、mysql索引结构的实现B+树/B-树原理
  4. python程序文件扩展名有_python程序文件的扩展名称是什么
  5. 无锁数据结构三:无锁数据结构的两大问题
  6. java视频流传输_目前在Web浏览器中流式传输实时视频的最佳做法?
  7. 2018计算机河北省高考试题,2018年河北高考物理压轴试题【含答案】
  8. mysql socket 与IP区别_MySQL本地用IP登陆而非socket
  9. 编辑电线标注及图纸上从主电源线上引出多条支路时如何进行线号的编写?
  10. python是基于什么原理_Python基于class()实现面向对象原理详解
  11. 关于T-SQL中exists或者not exists子查询的“伪优化”的做法
  12. POJ-2414 Phylogenetic Trees Inherited 状态压缩,位运算处理集合操作
  13. 深度学习中所有的优化器的详细介绍与列表化对比分析
  14. 视觉在无人驾驶中的应用及分类_紫外光在机器视觉中的应用
  15. 怎样关闭vivo的HTML查看器,vivo安全模式在哪儿关闭?
  16. JAVA北京时间转换为世界协调时
  17. 基于 WT2003H0语音芯片在扫地机/智能桌游等产品更换语音bin应用设计方案介绍
  18. 遥想大肠包小肠----python装饰器乱弹
  19. CoolWeather地区无法加载
  20. 微信小程序——用户登录模块服务器搭建

热门文章

  1. apt java8_Ubuntu 18.04安装Java JDK8三种方式
  2. MySQL8web安装_mysql 8.0.18 安装配置图文教程
  3. 计算机会计简要回答会计电算化的作用,会计电算化在学校财务管理中的作用
  4. 百度编辑器的初步使用
  5. 史上最全的ASP.NET MVC路由配置,以后RouteConfig再弄不懂神仙都难救你啦~
  6. 基于Consul的数据库高可用架构【转】
  7. hdu 6149 Valley Numer II(01背包套状压dp)
  8. package.json和bower的参数解释
  9. Halcon的应用程序 打开后 弹出没有帮助文件错误提示
  10. 虚拟仿真引擎消息机制