java线程和进程(阻塞队列)

2023-05-16

目录

1.阻塞队列简介

2Java中的阻塞队列

3.阻塞队列的实现原理 

4.阻塞队列的使用场景


1.阻塞队列简介

         阻塞队列常用于生产者和消费者的场景, 生产者是往队列里添加元素的线程, 消费者是从队列里拿元素的线程。 阻塞队列就是生产者存放元素的容器, 而消费者也只从容器里拿元素。


1.1.常见阻塞场景

         阻塞队列有两个常见的阻塞场景, 它们分别是:
        (1) 当队列中没有数据的情况下, 消费者端的所有线程都会被自动阻塞(挂起) , 直到有数据放入队列。
        (2) 当队列中填满数据的情况下, 生产者端的所有线程都会被自动阻塞(挂起) , 直到队列中有空的位置, 线程被自动唤醒。
        支持以上两种阻塞场景的队列被称为阻塞队列。

1.2.BlockingQueue的核心方法

放入数据:
        • offer(anObject) : 表示如果可能的话, 将anObject加到BlockingQueue里。 即如果BlockingQueue可以容纳, 则返回true, 否则返回false。 (本方法不阻塞当前执行方法的线程。 )
        • offer(E o, long timeout, TimeUnit unit) : 可以设定等待的时间。 如果在指定的时间内还不能往队列
中加入BlockingQueue, 则返回失败。
        • put(anObject) : 将anObject加到BlockingQueue里。 如果BlockQueue没有空间, 则调用此方法的线程被阻断, 直到BlockingQueue里面有空间再继续。
获取数据:
        • poll(time) : 取走 BlockingQueue 里排在首位的对象。 若不能立即取出, 则可以等 time参数规定的时间。 取不到时返回null。
        • poll(long timeout, TimeUnit unit) : 从BlockingQueue中取出一个队首的对象。 如果在指定时间内,队列一旦有数据可取, 则立即返回队列中的数据; 否则直到时间超时还没有数据可取, 返回失败。
        • take() : 取走BlockingQueue里排在首位的对象。 若BlockingQueue为空, 则阻断进入等待状态, 直到 BlockingQueue有新的数据被加入。
        • drainTo() : 一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数) 。 通过该方法, 可以提升获取数据的效率; 无须多次分批加锁或释放锁。

2Java中的阻塞队列

        在Java中提供了7个阻塞队列, 它们分别如下所示。
        • ArrayBlockingQueue: 由数组结构组成的有界阻塞队列。
        • LinkedBlockingQueue: 由链表结构组成的有界阻塞队列。
        • PriorityBlockingQueue: 支持优先级排序的无界阻塞队列。
        • DelayQueue: 使用优先级队列实现的无界阻塞队列。
        • SynchronousQueue: 不存储元素的阻塞队列。
        • LinkedTransferQueue: 由链表结构组成的无界阻塞队列。
        • LinkedBlockingDeque: 由链表结构组成的双向阻塞队列。


        下面分别介绍这些阻塞队列

        2.1.ArrayBlockingQueue它是用数组实现的有界阻塞队列, 并按照先进先出(FIFO) 的原则对元素进行排序。 默认情况下不保证线程公平地访问队列。 公平访问队列就是指阻塞的所有生产者线程或消费者线程, 当队列可用时, 可以按照阻塞的先后顺序访问队列。 即先阻塞的生产者线程, 可以先往队列里插入元素; 先阻塞的消费者线程, 可以先从队列里获取元素。 通常情况下为了保证公平性会降低吞吐量。 我们可以使用以下代码创建一个公平的阻塞队列, 如下所示:
ArrayBlockingQueue fairQueue=new ArrayBlockingQueue(2000,true);
        2.2.LinkedBlockingQueue
它是基于链表的阻塞队列, 同ArrayListBlockingQueue类似, 此队列按照先进先出(FIFO) 的原则对元素进行排序, 其内部也维持着一个数据缓冲队列(该队列由一个链表构成) 。 当生产者往队列中放入一个数据时, 队列会从生产者手中获取数据, 并缓存在队列内部, 而生产者立即返回; 只有当队列缓冲区达到缓存容量的最大值时(LinkedBlockingQueue可以通过构造方法指定该值) , 才会阻塞生产者队列, 直到消费者从队列中消费掉一份数据, 生产者线程会被唤醒。 反之, 对于消费者这端的处理也基于同样的原理。而LinkedBlockingQueue之所以能够高效地处理并发数据, 还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步。 这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据, 以此来提高整个队列的并发性能。 作为开发者, 我们需要注意的是, 如果构造一个LinkedBlockingQueue对象, 而没有指定其容量大小, LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE) 。 这样一来, 如果生产者的速度一旦大于消费者的速度, 也许还没有等到队列满阻塞产生, 系统内存就有可能已被消耗殆尽了。 ArrayBlockingQueue和LinkedBlockingQueue是两个最普通也是最常用的阻塞队列。 一般情况下, 在处理多线程间的生产者-消费者问题时, 使用这两个类足已。
       2. 3.PriorityBlockingQueue它是一个支持优先级的无界队列。 默认情况下元素采取自然顺序升序排列。 这里可以自定义实现compareTo() 方法来指定元素进行排序规则; 或者初始化PriorityBlockingQueue时, 指定构造参数Comparator来对元素进行排序。 但其不能保证同优先级元素的顺序。
       2. 4.DelayQueue它是一个支持延时获取元素的无界阻塞队列。 队列使用PriorityQueue来实现。 队列中的元素必须实现Delayed 接口。 创建元素时, 可以指定元素到期的时间, 只有在元素到期时才能从队列中取走。
        2.5.SynchronousQueue它是一个不存储元素的阻塞队列。 每个插入操作必须等待另一个线程的移除操作, 同样任何一个移除操作都等待另一个线程的插入操作。 因此此队列内部其实没有任何一个元素, 或者说容量是0, 严格来说它并不是一种容器。 由于队列没有容量, 因此不能调用peek操作(返回队列的头元素) 。
       2. 6.LinkedTransferQueue它是一个由链表结构组成的无界阻塞TransferQueue队列。 LinkedTransferQueue实现了一个重要的接口TransferQueue。 该接口含有5个方法, 其中有3个重要的方法, 它们分别如下所示。
        (1) transfer(E e) : 若当前存在一个正在等待获取的消费者线程, 则立刻将元素传递给消费者; 如果没有消费者在等待接收数据, 就会将元素插入到队列尾部, 并且等待进入阻塞状态, 直到有消费者线程取走该元素。
        (2) tryTransfer(E e) : 若当前存在一个正在等待获取的消费者线程, 则立刻将元素传递给消费者;若不存在, 则返回 false, 并且不进入队列, 这是一个不阻塞的操作。 与 transfer 方法不同的是, tryTransfer方法无论消费者是否接收, 其都会立即返回; 而transfer方法则是消费者接收了才返回。                                                                                                                              (3) tryTransfer(E e, long timeout, TimeUnit unit) : 若当前存在一个正在等待获取的消费者线程,则立刻将元素传递给消费者; 若不存在则将元素插入到队列尾部, 并且等待消费者线程取走该元素。 若在指定的超时时间内元素未被消费者线程获取, 则返回false; 若在指定的超时时间内其被消费者线程获取,则返回true。
       2. 7.LinkedBlockingDeque它是一个由链表结构组成的双向阻塞队列。 双向队列可以从队列的两端插入和移出元素, 因此在多线程同时入队时, 也就减少了一半的竞争。 由于是双向的, 因此LinkedBlockingDeque多了addFirst、 addLast、offerFirst、 offerLast、 peekFirst、 peekLast等方法。 其中, 以First单词结尾的方法, 表示插入、 获取或移除双端队列的第一个元素; 以Last单词结尾的方法, 表示插入、 获取或移除双端队列的最后一个元素。

3.阻塞队列的实现原理 

以ArrayBlockingQueue为例, 我们先来看看代码, 如下所示:

 从上面的代码可以看出 ArrayBlockingQueue 是维护一个 Object 类型的数组, takeIndex 和putIndex分别表示队首元素和队尾元素的下标, count表示队列中元素的个数, lock则是一个可重入锁, notEmpty和notFull是等待条件。 接下来我们看看关键方法put, 代码如下所示:

 从 put 方法的实现可以看出, 它先获取了锁, 并且获取的是可中断锁, 然后判断当前元素个数是
组的长度, 如果相等, 则调用notFull.await() 进行等待。 当此线程被其他线程唤醒时, 通过
queue(e) 方法插入元素, 最后解锁。 接下来看看enqueue(e) 方法, 如下所示:

 插入成功后, 通过notEmpty唤醒正在等待取元素的线程。 再来看看take方法。

跟put方法实现类似, put方法等待的是notFull信号, 而take方法等待的是notEmpty信号。 在take方法中,如果可以取元素, 则通过dequeue方法取得元素。 下面是dequeue方法的实现。

 跟enqueue方法类似, 在获取元素后, 通过notFull的signal方法来唤醒正在等待插入元素的线程。

4.阻塞队列的使用场景

        除了线程池的实现使用阻塞队列外, 我们还可以在生产者-消费者模式中使用阻塞队列: 首先使用Object.wait() 、 Object.notify() 和非阻塞队列实现生产者-消费者模式, 代码如下所示:

 

 下面是使用阻塞队列实现的生产者-消费者模式。

 很显然, 使用阻塞队列实现无须单独考虑同步和线程间通信的问题, 其实现起来很简单。


 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java线程和进程(阻塞队列) 的相关文章

随机推荐

  • asp连接Mssql的方法及常见的错误

    asp衔接Mssql的办法及常见的差错 第一种写法 xff1a MM conn STRING 61 34 Driver 61 SQL Server server 61 local uid 61 sa pwd 61 database 61 i
  • SQL一个存储过程调用另一个存储过程 获得返回值问题

    第一种方法 使用output参数 USE AdventureWorks GO IF OBJECT ID 39 Production usp GetList 39 39 P 39 IS NOT NULL DROP PROCEDURE Prod
  • 第5章 Linux利器之VIM(二)

    继续讲vim的操作 vim的功能很强大 xff0c 大家一定要多练习 xff0c 用的久了自然就熟练了 xff0c 其次是要多使用快捷键 xff0c 少用鼠标 xff0c 遇到不会操作的去百度查一查这样你记的快捷操作就越来越多了 上第二张神
  • -----------------------------------SQL2005的安装问题汇总 --------------------------

    SQL2005的安装问题汇总 SQL2005 分五个版本 xff0c 如下所列 xff0c 1 Enterprise 企业版 2 Development 开发版 3 Workgroup 工作群版 4 Standard 标准版 5 Expre
  • ----------------50条常用语句(以学生表为例)---------

    Student S Sname Sage Ssex 学生表 Course C Cname T 课程表 SC S C score 成绩表 Teacher T Tname 教师表 问题 xff1a 1 查询 课程比 课程成绩高的所有学生的学号
  • --------c# SQL数据库远程连接及配置方法---------------

    一 xff1a C 连接SQL数据库 Data Source 61 myServerAddress Initial Catalog 61 myDataBase User Id 61 myUsername Password 61 myPass
  • 一些有用的DMV

    xfeff xfeff 查看数据库中正在运行的SQL语句的执行情况 xff08 包括执行计划和等待类型等等 xff09 SELECT sp ecid DB NAME sp dbid AS Database QP query plan sp
  • Bulk insert如何导入部分字段的数据

    xfeff xfeff IF OBJECT ID 39 Employee 39 IS NOT NULL DROP TABLE Employee GO CREATE TABLE Employee Id int Name VARCHAR 100
  • SQL SERVER获取索引脚本

    xfeff xfeff 关于如何获取索引脚本的语句很多 xff0c 上次在项目中需要去查询并获取索引脚本 xff0c 所以写了一个简单的查询语句来进行获取 WITH idxcol AS SELECT i object id i index
  • 超详细一文到底!软件测试基本流程

    前言 xff1a 采用通用的测试流程 xff0c 能高效 高质量的完成软件测试工作 xff0c 有助于减少沟通成本 xff0c 对各阶段产出有明确认知等等 最终目标 xff1a 实现软件测试规范化 标准化 以下为非通用标准 xff0c 仅供
  • shell:重启&&关机

    文章目录 shutdownhaltpoweroffrebootinitsync shutdown 关机重启命令 shutdown h 10十分钟后关机shutdown h 0马上关机shutdown h now马上关机shutdown c取
  • 世界上最经典的25句话

    1 记住该记住的 xff0c 忘记该忘记的 改变能改变的 xff0c 接受不能改变的 2 能冲刷一切的除了眼泪 xff0c 就是时间 xff0c 以时间来推移感情 xff0c 时间越长 xff0c 冲突越淡 xff0c 仿佛不断稀释的茶 3
  • Android Intent 用法总结

    From xff1a https www jianshu com p 67d99a82509b Android 中提供了 Intent 机制来协助应用间的交互与通讯 xff0c Intent 负责对应用中一次操作的动作 动作涉及数据 附加数
  • centos8 配置vsftpd的SSL/TLS功能

    前面我带着大家已经配置了一个vsftpd服务器 xff08 虚拟用户模式 xff09 xff0c 匿名用户和本地用户模式配置起来比较简单就没再赘述 xff0c 本文再带大家开启vsftpd的SSL TLS功能 1 生成一个TLS证书 Vsf
  • ubuntu gnome 桌面增加快捷方式

    方法 在 ubuntu 的桌面增加快捷方式很简单 xff0c 在 usr share applications 下 xff0c 增加一个 desktop 文件就可 xff0c 内容基本如下 xff1a Desktop Entry Versi
  • DOS那一代的程序员现在都干嘛呢?

    亿友论坛 DOS那一代的程序员现在都干嘛呢 xff1f xff08 几年前的老帖子 xff09 作者 xff1a 包子夹蛋 发布时间 xff1a 2005 5 10 14 01 00 DOS那一代的程序员现在都干嘛呢 xff1f xff08
  • KVM虚拟机掉电重启后无法ssh连接访问

    KVM虚拟机掉电重启无法ssh连接访问 问题描述问题解决过程解决方法 问题描述 KVM虚拟机升级ssh版本后 xff0c 主机因为维护掉电 xff0c 等主机上电后 xff0c 虚拟机重启后 xff0c 再也无法ssh连接访问 问题解决过程
  • 让Ubuntu 18.04系统支持root用户登录的方法

    简介 默认的Ubuntu 18 04系统在登陆界面上是不支持root用户直接登录的 xff0c 但是你可以使用下面的方法让Ubuntu 18 04也支持root登录 通常情况下 xff0c 在Ubuntu 18 04中的普通用户只能通过运行
  • 这十个css动画案例惊艳众人

    大家好 我是前端实验室的大师兄 对于网页设计师和开发工程师而言 xff0c 创建一款极具趣味性和实用性的CSS网页动画 xff0c 能让网站美观不少 CSS动画 xff0c 就是通过CSS代码搭建网页动画 允许设计师和开发人员 xff0c
  • java线程和进程(阻塞队列)

    目录 1 阻塞队列简介 2Java中的阻塞队列 3 阻塞队列的实现原理 4 阻塞队列的使用场景 1 阻塞队列简介 阻塞队列常用于生产者和消费者的场景 xff0c 生产者是往队列里添加元素的线程 xff0c 消费者是从队列里拿元素的线程 阻塞