iis服务器助手广告
返回顶部
首页 > 资讯 > 精选 >Java阻塞队列BlockingQueue怎么使用
  • 337
分享到

Java阻塞队列BlockingQueue怎么使用

2023-07-02 11:07:48 337人浏览 安东尼
摘要

本篇内容介绍了“Java阻塞队列BlockingQueue怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一. 前言在新增的Concu

本篇内容介绍了“Java阻塞队列BlockingQueue怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一. 前言

在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。

二. 认识BlockingQueue

阻塞队列,顾名思义,首先它是一个队列,而一个队列在数据结构中所起的作用大致如下图所示:

Java阻塞队列BlockingQueue怎么使用

从上图我们可以很清楚看到,通过一个共享的队列,可以使得数据由队列的一端输入,从另外一端输出;

常用的队列主要有以下两种:(当然通过不同的实现方式,还可以延伸出很多不同类型的队列,DelayQueue就是其中的一种)

先进先出(FIFO):先插入的队列的元素也最先出队列,类似于排队的功能。从某种程度上来说这种队列也体现了一种公平性。

后进先出(LIFO):后插入队列的元素最先出队列,这种队列优先处理最近发生的事件。  

多线程环境中,通过队列可以很容易实现数据共享,比如经典的“生产者”和“消费者”模型中,通过队列可以很便利地实现两者之间的数据共享。假设我们有若干生产者线程,另外又有若干个消费者线程。如果生产者线程需要把准备好的数据共享给消费者线程,利用队列的方式来传递数据,就可以很方便地解决他们之间的数据共享问题。但如果生产者和消费者在某个时间段内,万一发生数据处理速度不匹配的情况呢?理想情况下,如果生产者产出数据的速度大于消费者消费的速度,并且当生产出来的数据累积到一定程度的时候,那么生产者必须暂停等待一下(阻塞生产者线程),以便等待消费者线程把累积的数据处理完毕,反之亦然。然而,在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。好在此时,强大的concurrent包横空出世了,而他也给我们带来了强大的BlockingQueue。(在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒),下面两幅图演示了BlockingQueue的两个常见阻塞场景:

Java阻塞队列BlockingQueue怎么使用

如上图所示:当队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放入队列。

Java阻塞队列BlockingQueue怎么使用

如上图所示:当队列中填满数据的情况下,生产者端的所有线程都会被自动阻塞(挂起),直到队列中有空的位置,线程被自动唤醒。

这也是我们在多线程环境下,为什么需要BlockingQueue的原因。作为BlockingQueue的使用者,我们再也不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办了。既然BlockingQueue如此神通广大,让我们一起来见识下它的常用方法:

三.BlockingQueue的核心方法:

放入数据

(1)offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false.(本方法不阻塞当前执行方法

的线程);      
(2)offer(E o, long timeout, TimeUnit unit):可以设定等待的时间,如果在指定的时间内,还不能往队列中加入BlockingQueue,则返回失败。

(3)put(anObject):把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续.

获取数据

(1)poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null;

(2)poll(long timeout, TimeUnit unit):从BlockingQueue取出一个队首的对象,如果在指定时间内,队列一旦有数据可取,则立即返回队列中的数据。否则知道时间

超时还没有数据可取,返回失败。

(3)take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被加入;

(4)drainTo():一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数),通过该方法,可以提升获取数据效率;不需要多次分批加或释放锁。

四.常见BlockingQueue

在了解了BlockingQueue的基本功能后,让我们来看看BlockingQueue家庭大致有哪些成员?

Java阻塞队列BlockingQueue怎么使用

ArrayBlockingQueue

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

ArrayBlockingQueue在生产者放入数据和消费者获取数据,都是共用同一个锁对象,由此也意味着两者无法真正并行运行,这点尤其不同于LinkedBlockingQueue;按照实现原理来分析,ArrayBlockingQueue完全可以采用分离锁,从而实现生产者和消费者操作的完全并行运行。Doug Lea之所以没这样去做,也许是因为ArrayBlockingQueue的数据写入和获取操作已经足够轻巧,以至于引入独立的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜。 ArrayBlockingQueue和LinkedBlockingQueue间还有一个明显的不同之处在于,前者在插入或删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的node对象。这在长时间内需要高效并发地处理大批量数据的系统中,其对于GC的影响还是存在一定的区别。而在创建ArrayBlockingQueue时,我们还可以控制对象的内部锁是否采用公平锁,默认采用非公平锁。

LinkedBlockingQueue

基于链表的阻塞队列,同ArrayListBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。而LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。

作为开发者,我们需要注意的是,如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE),这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。

ArrayBlockingQueue和LinkedBlockingQueue是两个最普通也是最常用的阻塞队列,一般情况下,在处理多线程间的生产者消费者问题,使用这两个类足以。

下面的代码演示了如何使用BlockingQueue:

(1) 测试

import java.util.concurrent.BlockingQueue;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.LinkedBlockingQueue;public class BlockingQueueTest {    public static void main(String[] args) throws InterruptedException {        // 声明一个容量为10的缓存队列        BlockingQueue<String> queue = new LinkedBlockingQueue<String>(10);        //new了三个生产者和一个消费者        Producer producer1 = new Producer(queue);        Producer producer2 = new Producer(queue);        Producer producer3 = new Producer(queue);        Consumer consumer = new Consumer(queue);        // 借助Executors        ExecutorService service = Executors.newCachedThreadPool();        // 启动线程        service.execute(producer1);        service.execute(producer2);        service.execute(producer3);        service.execute(consumer);        // 执行10s        Thread.sleep(10 * 1000);        producer1.stop();        producer2.stop();        producer3.stop();        Thread.sleep(2000);        // 退出Executor        service.shutdown();    }}

(2)生产者类

import java.util.Random;import java.util.concurrent.BlockingQueue;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger;public class Producer implements Runnable {    private volatile boolean  isRunning = true;//是否在运行标志    private BlockingQueue queue;//阻塞队列    private static AtomicInteger count = new AtomicInteger();//自动更新的值    private static final int DEFAULT_RANGE_FOR_SLEEP = 1000;    //构造函数    public Producer(BlockingQueue queue) {        this.queue = queue;    }    public void run() {        String data = null;        Random r = new Random();        System.out.println("启动生产者线程!");        try {            while (isRunning) {                System.out.println("正在生产数据...");                Thread.sleep(r.nextInt(DEFAULT_RANGE_FOR_SLEEP));//取0~DEFAULT_RANGE_FOR_SLEEP值的一个随机数                data = "data:" + count.incrementAndGet();//以原子方式将count当前值加1                System.out.println("将数据:" + data + "放入队列...");                if (!queue.offer(data, 2, TimeUnit.SECONDS)) {//设定的等待时间为2s,如果超过2s还没加进去返回true                    System.out.println("放入数据失败:" + data);                }            }        } catch (InterruptedException e) {            e.printStackTrace();            Thread.currentThread().interrupt();        } finally {            System.out.println("退出生产者线程!");        }    }    public void stop() {        isRunning = false;    }}

(3)消费者类

import java.util.Random;import java.util.concurrent.BlockingQueue;import java.util.concurrent.TimeUnit;public class Consumer implements Runnable {    private BlockingQueue<String> queue;    private static final int DEFAULT_RANGE_FOR_SLEEP = 1000;    //构造函数    public Consumer(BlockingQueue<String> queue) {        this.queue = queue;    }    public void run() {        System.out.println("启动消费者线程!");        Random r = new Random();        boolean isRunning = true;        try {            while (isRunning) {                System.out.println("正从队列获取数据...");                String data = queue.poll(2, TimeUnit.SECONDS);//有数据时直接从队列的队首取走,无数据时阻塞,在2s内有数据,取走,超过2s还没数据,返回失败                if (null != data) {                    System.out.println("拿到数据:" + data);                    System.out.println("正在消费数据:" + data);                    Thread.sleep(r.nextInt(DEFAULT_RANGE_FOR_SLEEP));                } else {                    // 超过2s还没数据,认为所有生产线程都已经退出,自动退出消费线程。                    isRunning = false;                }            }        } catch (InterruptedException e) {            e.printStackTrace();            Thread.currentThread().interrupt();        } finally {            System.out.println("退出消费者线程!");        }    }}

DelayQueue

DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue是一个没有大小限制的队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。

使用场景:

DelayQueue使用场景较少,但都相当巧妙,常见的例子比如使用一个DelayQueue来管理一个超时未响应的连接队列。

PriorityBlockingQueue

基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定),但需要注意的是PriorityBlockingQueue并不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者。因此使用的时候要特别注意,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁。

SynchronousQueue

一种无缓冲的等待队列,类似于无中介的直接交易,有点像原始社会中的生产者和消费者,生产者拿着产品去集市销售给产品的最终消费者,而消费者必须亲自去集市找到所要商品的直接生产者,如果一方没有找到合适的目标,那么对不起,大家都在集市等待。相对于有缓冲的BlockingQueue来说,少了一个中间经销商的环节(缓冲区),如果有经销商,生产者直接把产品批发给经销商,而无需在意经销商最终会将这些产品卖给那些消费者,由于经销商可以库存一部分商品,因此相对于直接交易模式,总体来说采用中间经销商的模式会吞吐量高一些(可以批量买卖);但另一方面,又因为经销商的引入,使得产品从生产者到消费者中间增加了额外的交易环节,单个产品的及时响应性能可能会降低。

声明一个SynchronousQueue有两种不同的方式,它们之间有着不太一样的行为。公平模式和非公平模式的区别:

如果采用公平模式:SynchronousQueue会采用公平锁,并配合一个FIFO队列来阻塞多余的生产者和消费者,从而体系整体的公平策略;

但如果是非公平模式(SynchronousQueue默认):SynchronousQueue采用非公平锁,同时配合一个LIFO队列来管理多余的生产者和消费者,而后一种模式,如果生产者和消费者的处理速度有差距,则很容易出现饥渴的情况,即可能有某些生产者或者是消费者的数据永远都得不到处理。

“Java阻塞队列BlockingQueue怎么使用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: Java阻塞队列BlockingQueue怎么使用

本文链接: https://www.lsjlt.com/news/341619.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java阻塞队列BlockingQueue怎么使用
    本篇内容介绍了“Java阻塞队列BlockingQueue怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一. 前言在新增的Concu...
    99+
    2023-07-02
  • Java阻塞队列BlockingQueue详解
    目录队列的类型数据结构阻塞队列 BlockingQueue常见的阻塞队列BlockingQueue APIArrayBlockingQueue 源码简解生产者消费者模式延迟队列 De...
    99+
    2024-04-02
  • Java 阻塞队列BlockingQueue详解
    目录一. 前言二. 认识BlockingQueue三.BlockingQueue的核心方法:四.常见BlockingQueue五. 小结一. 前言 在新增的Concurrent包中,...
    99+
    2024-04-02
  • Java阻塞队列BlockingQueue怎么实现
    这篇文章主要讲解了“Java阻塞队列BlockingQueue怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java阻塞队列BlockingQueue怎么实现”吧!BlockingQ...
    99+
    2023-06-02
  • Java阻塞队列BlockingQueue基础与使用
    目录什么是阻塞队列阻塞队列的特点BlockingQueue不是新的东西学会使用队列SynchronousQueue 同步队列什么是阻塞队列 阻塞队列本质上还是一种队列,遵循先进先出,...
    99+
    2023-01-03
    Java阻塞队列 Java BlockingQueue
  • java 中 阻塞队列BlockingQueue详解及实例
    java 中 阻塞队列BlockingQueue详解及实例BlockingQueue很好的解决了多线程中数据的传输,首先BlockingQueue是一个接口,它大致有四个实现类,这是一个很特殊的队列,如果BlockQueue是空的,从Blo...
    99+
    2023-05-31
    java 阻塞队列 blockingqueue
  • 详解Java阻塞队列(BlockingQueue)的实现原理
    阻塞队列 (BlockingQueue)是Java util.concurrent包下重要的数据结构,BlockingQueue提供了线程安全的队列访问方式:当阻塞队列进行插入数据时,如果队列已满,线程将会阻塞等待直到队列非满;从阻塞队列取...
    99+
    2023-05-31
    java 阻塞队列 ava
  • Java并发编程之阻塞队列(BlockingQueue)详解
    目录队列阻塞队列ArrayBlockingQueue重要属性构造方法添加元素add(e)offer(e)put(e)offer(e,time,unit)移除元素take()deque...
    99+
    2024-04-02
  • Java并发队列BlockingQueue怎么用
    这篇文章主要介绍了Java并发队列BlockingQueue怎么用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java并发队列BlockingQueue怎么用文章都会有所收获,下面我们一起来看看吧。Block...
    99+
    2023-06-02
  • 详解Java中的阻塞队列
    什么是阻塞队列 在数据结构中,队列遵循FIFO(先进先出)原则。在java中,Queue接口定义了定义了基本行为,由子类完成实现,常见的队列有ArrayDeque、LinkedLis...
    99+
    2024-04-02
  • Java阻塞队列的实现及应用
    目录1.手写生产者消费者模型2.手写定时器总结1.手写生产者消费者模型 所谓生产者消费者模型,可以用我们生活中的例子来类比:我去一个小摊儿买吃的,老板把已经做好的小吃都放在摆盘上,供...
    99+
    2024-04-02
  • Java中怎么利用阻塞队列实现搜索
    这期内容当中小编将会给大家带来有关Java中怎么利用阻塞队列实现搜索,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。队列以一种先进先出的方式管理数据。如果你试图向一个已经满了的阻塞队列中添加一个元素,或是从...
    99+
    2023-06-17
  • Java常见的阻塞队列总结
    Java阻塞队列 阻塞队列和普通队列主要区别在阻塞二字: 阻塞添加:队列已满时,添加元素线程会阻塞,直到队列不满时才唤醒线程执行添加操作 阻塞删除:队列元素为空时,...
    99+
    2024-04-02
  • 详解Java七大阻塞队列之SynchronousQueue
    目录分析其实SynchronousQueue 是一个特别有意思的阻塞队列,就我个人理解来说,它很重要的特点就是没有容量。 直接看一个例子: package dongguabai....
    99+
    2024-04-02
  • 如何在Java中实现阻塞队列
    如何在Java中实现阻塞队列?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。Java阻塞队列阻塞队列和普通队列主要区别在阻塞二字:阻塞添加:队列已满时,添加元素线...
    99+
    2023-06-15
  • Java多线程案例之阻塞队列
    文章目录 一. 认识阻塞队列1. 什么是阻塞队列2. 生产者消费者模型3. 标准库中阻塞队列类 二. 基于循环队列实现的简单阻塞队列1. 循环队列的简单实现2. 阻塞队列的简单实现 ...
    99+
    2023-09-01
    java 面试 阻塞队列 生产者消费者模型 多线程
  • Java阻塞队列必看类:BlockingQueue快速了解大体框架和实现思路
    目录前言BlockingQueue概览主要实现类BlockingQueue方法的四类形式AbstractQueue概览源码解析总结前言 本文主要说明BlockingQueue类、阻塞...
    99+
    2022-11-13
    Java阻塞队列 Java BlockingQueue 阻塞队列BlockingQueue
  • Java面试必备之AQS阻塞队列和条件队列
    一.AQS入队规则 我们仔细分析一下AQS是如何维护阻塞队列的,在独占方式获取资源的时候,是怎么将竞争锁失败的线程丢到阻塞队列中的呢? 我们看看acquire方法,这里首先会调用子类...
    99+
    2024-04-02
  • Java中常用阻塞队列的问题是什么
    本篇内容主要讲解“Java中常用阻塞队列的问题是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java中常用阻塞队列的问题是什么”吧!Java常用阻塞队列ArrayBlockingQueue...
    99+
    2023-06-29
  • Java阻塞队列的实现原理是什么
    本篇文章给大家分享的是有关Java阻塞队列的实现原理是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。BlockingQueue接口提供了3个添加元素方法:add:添加元素到...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作