19.并发容器之BlockingQueue
1. BlockingQueue简介
在实际编程中,会经常使用到JDK中Collection集合框架中的各种容器类如实现List,Map,Queue接口的容器类,但是这些容器类基本上不是线程安全的,除了使用Collections可以将其转换为线程安全的容器,Doug Lea大师为我们都准备了对应的线程安全的容器,如实现List接口的CopyOnWriteArrayList(关于CopyOnWriteArrayList可以看这篇文章),实现Map接口的ConcurrentHashMap(关于ConcurrentHashMap可以看这篇文章),实现Queue接口的ConcurrentLinkedQueue(关于ConcurrentLinkedQueue可以看这篇文章)。
最常用的"生产者-消费者"问题中,队列通常被视作线程间操作的数据容器,这样,可以对各个模块的业务功能进行解耦,生产者将“生产”出来的数据放置在数据容器中,而消费者仅仅只需要在“数据容器”中进行获取数据即可,这样生产者线程和消费者线程就能够进行解耦,只专注于自己的业务功能即可。阻塞队列(BlockingQueue)被广泛使用在“生产者-消费者”问题中,其原因是BlockingQueue提供了可阻塞的插入和移除的方法。当队列容器已满,生产者线程会被阻塞,直到队列未满;当队列容器为空时,消费者线程会被阻塞,直至队列非空时为止。
2. 基本操作
BlockingQueue基本操作总结如下(此图来源于JAVA API文档):
BlockingQueue继承于Queue接口,因此,对数据元素的基本操作有:
插入元素
add(E e) :往队列插入数据,当队列满时,插入元素时会抛出IllegalStateException异常;
offer(E e):当往队列插入数据时,插入成功返回
true
,否则则返回false
。当队列满时不会抛出异常;
删除元素
remove(Object o):从队列中删除数据,成功则返回
true
,否则为false
poll:删除数据,当队列为空时,返回null;
查看元素
element:获取队头元素,如果队列为空时则抛出NoSuchElementException异常;
peek:获取队头元素,如果队列为空则抛出NoSuchElementException异常
BlockingQueue具有的特殊操作:
插入数据:
put():当阻塞队列容量已经满时,往阻塞队列插入数据的线程会被阻塞,直至阻塞队列已经有空余的容量可供使用;
offer(E e, long timeout, TimeUnit unit):若阻塞队列已经满时,同样会阻塞插入数据的线程,直至阻塞队列已经有空余的地方,与put方法不同的是,该方法会有一个超时时间,若超过当前给定的超时时间,插入数据的线程会退出;
删除数据
take():当阻塞队列为空时,获取队头数据的线程会被阻塞;
poll(long timeout, TimeUnit unit):当阻塞队列为空时,获取数据的线程会被阻塞,另外,如果被阻塞的线程超过了给定的时长,该线程会退出
3. 常用的BlockingQueue
实现BlockingQueue接口的有ArrayBlockingQueue, LinkedBlockingQueue , PriorityBlockingQueue, DelayQueue,LinkedTransferQueue, SynchronousQueue, LinkedBlockingDeque,而这几种常见的阻塞队列也是在实际编程中会常用的,下面对这几种常见的阻塞队列进行说明:
1.ArrayBlockingQueue
ArrayBlockingQueue是由数组实现的有界阻塞队列。该队列命令元素FIFO(先进先出)。因此,队头元素保存的是队列中存在时间最长的数据元素,而队尾数据保存的则是当前队列最新的数据元素。ArrayBlockingQueue可作为“有界数据缓冲区”,生产者插入数据到队列容器中,并由消费者提取。ArrayBlockingQueue一旦创建,容量不能改变。
当队列容量满时,尝试将元素放入队列将导致操作阻塞;尝试从一个空队列中取一个元素也会同样阻塞。
ArrayBlockingQueue默认情况下不能保证线程访问队列的公平性,所谓公平性是指严格按照线程等待的绝对时间顺序,即最先等待的线程能够最先访问到ArrayBlockingQueue。而非公平性则是指访问ArrayBlockingQueue的顺序不是遵守严格的时间顺序,有可能存在,一旦ArrayBlockingQueue可以被访问时,长时间阻塞的线程依然无法访问到ArrayBlockingQueue。如果保证公平性,通常会降低吞吐量。如果需要获得公平性的ArrayBlockingQueue,可采用如下代码:
private static ArrayBlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(10,true);
关于ArrayBlockingQueue的实现原理,可以看这篇文章。
2.LinkedBlockingQueue
LinkedBlockingQueue是用链表实现的有界阻塞队列,同样满足FIFO的特性,与ArrayBlockingQueue相比起来具有更高的吞吐量,为了防止LinkedBlockingQueue容量迅速增,损耗大量内存。通常在创建LinkedBlockingQueue对象时,会指定其大小,如果未指定,容量等于Integer.MAX_VALUE
3.PriorityBlockingQueue
PriorityBlockingQueue是一个支持优先级的无界阻塞队列。默认情况下元素采用自然顺序进行排序,也可以通过自定义类实现compareTo()方法来指定元素排序规则,或者初始化时通过构造器参数Comparator来指定排序规则。
4.SynchronousQueue
SynchronousQueue每个插入操作必须等待另一个线程进行相应的删除操作,因此,SynchronousQueue实际上没有存储任何数据元素,因为只有线程在删除数据时,其他线程才能插入数据,同样的,如果当前有线程在插入数据时,线程才能删除数据。SynchronousQueue也可以通过构造器参数来为其指定公平性。
5.LinkedTransferQueue
LinkedTransferQueue是一个由链表数据结构构成的无界阻塞队列,由于该队列实现了TransferQueue接口,与其他阻塞队列相比主要有以下不同的方法:
transfer(E e) 如果当前有线程(消费者)正在调用take()方法或者可延时的poll()方法进行消费数据时,生产者线程可调用transfer方法将数据传递给消费者线程。如果当前没有消费者线程的话,生产者线程就会将数据插入到队尾,直到有消费者能够进行消费才能退出;
tryTransfer(E e) tryTransfer方法如果当前有消费者线程(调用take方法或者具有超时特性的poll方法)正在消费数据的话,该方法可以将数据立即传送给消费者线程,如果当前没有消费者线程消费数据的话,就立即返回false
。因此,与transfer方法相比,transfer方法是必须等到有消费者线程消费数据时,生产者线程才能够返回。而tryTransfer方法能够立即返回结果退出。
tryTransfer(E e,long timeout,imeUnit unit)</br> 与transfer基本功能一样,只是增加了超时特性,如果数据在规定的超时时间内没有消费者进行消费的话,就返回false
。
6.LinkedBlockingDeque
LinkedBlockingDeque是基于链表数据结构的有界阻塞双端队列,如果在创建对象时为指定大小时,其默认大小为Integer.MAX_VALUE。与LinkedBlockingQueue相比,主要的不同点在于,LinkedBlockingDeque具有双端队列的特性。LinkedBlockingDeque基本操作如下图所示(来源于java文档)
如上图所示,LinkedBlockingDeque的基本操作可以分为四种类型:1.特殊情况,抛出异常;2.特殊情况,返回特殊值如null或者false;3.当线程不满足操作条件时,线程会被阻塞直至条件满足;4. 操作具有超时特性。
另外,LinkedBlockingDeque实现了BlockingDueue接口而LinkedBlockingQueue实现的是BlockingQueue,这两个接口的主要区别如下图所示(来源于java文档):
从上图可以看出,两个接口的功能是可以等价使用的,比如BlockingQueue的add方法和BlockingDeque的addLast方法的功能是一样的。
7.DelayQueue
DelayQueue是一个存放实现Delayed接口的数据的无界阻塞队列,只有当数据对象的延时时间达到时才能插入到队列进行存储。如果当前所有的数据都还没有达到创建时所指定的延时期,则队列没有队头,并且线程通过poll等方法获取数据元素则返回null。所谓数据延时期满时,则是通过Delayed接口的getDelay(TimeUnit.NANOSECONDS)
来进行判定,如果该方法返回的是小于等于0则说明该数据元素的延时期已满。
应用场景
(1)数据缓存 (2)定时任务
19.并发容器之BlockingQueue的更多相关文章
- Java并发编程:并发容器之ConcurrentHashMap(转)
本文转自:http://www.cnblogs.com/dolphin0520/p/3932905.html Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载 ...
- 11、Java并发编程:并发容器之CopyOnWriteArrayList
Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...
- 10、Java并发编程:并发容器之ConcurrentHashMap
Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载自: http://www.haogongju.net/art/2350374 JDK5中添加了新的concu ...
- 并发容器之ConcurrentHashMap(转载)
Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载自: http://www.haogongju.net/art/2350374 1.7与1.8有很大的区别:h ...
- Java并发编程:并发容器之CopyOnWriteArrayList(转载)
Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...
- Java并发编程:并发容器之ConcurrentHashMap(转载)
Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载自: http://www.haogongju.net/art/2350374 JDK5中添加了新的concu ...
- Java并发编程:并发容器之ConcurrentHashMap
转载: Java并发编程:并发容器之ConcurrentHashMap JDK5中添加了新的concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的 ...
- Java并发编程:并发容器之CopyOnWriteArrayList
转载: Java并发编程:并发容器之CopyOnWriteArrayList Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个 ...
- 【转】Java并发编程:并发容器之CopyOnWriteArrayList
Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改, ...
随机推荐
- mariadb多源复制 muiltil source replication
环境:centos-6.5 Mariadb:10.1.13-MariaDB 多源复制:企业数据库中写的需求较大,以至于I/O负载比较大,那么就必须把写的操作分摊到多台主服务器上进行,然后在将 ...
- python全栈开发从入门到放弃之模块和包
一 模块 1 什么是模块? 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 但其实import加载的模块分为四个通用类别: 1 使用python编 ...
- python阳历转阴历,阴历转阳历
#!/usr/bin/env python # coding:utf8 # author:Z time:2019/1/16 import sxtwl # 日历中文索引 ymc = [u"十一 ...
- HDU - 4609 3-idiots (FFT+母函数)
题意:给N个数,求任意选三个数能构成三角形的概率 分析:枚举两条边之和的复杂度\(O(N^2)\),显然不行,所以要更高效地做到枚举出两边之和. 所以用生成函数搭配FFT在\(O(NlogN)\)的时 ...
- spark[源码]-sparkContext详解[一]
spark简述 sparkContext在Spark应用程序的执行过程中起着主导作用,它负责与程序和spark集群进行交互,包括申请集群资源.创建RDD.accumulators及广播变量等.spar ...
- linux卸载mysql
第二.停止MYSQL运行以及卸载老版本 service mysqld stop #暂停MYSQL yum remove mysql mysql-* #卸载老版本MYSQL 通过上面的命令,我们先停止 ...
- Python面试题目之Python的复制和赋值浅析
python采用的是引用变量的结构,也就说如果你对一个变量赋值,并不是给这个变量开辟了一块内存空间而是将一个对象的内存空间地址告诉了这个变量,这样做的好处是便于管理,节省内存空间,便于内存释放等等.但 ...
- 如何在Kubernetes集群动态使用 NAS 持久卷
1. 介绍: 本文介绍的动态生成NAS存储卷的方案:在一个已有文件系统上,自动生成一个目录,这个目录定义为目标存储卷: 镜像地址:registry.cn-hangzhou.aliyuncs.com/a ...
- MySQL的索引是什么?怎么优化?
索引类似大学图书馆建书目索引,可以提高数据检索的效率,降低数据库的IO成本.MySQL在300万条记录左右性能开始逐渐下降,虽然官方文档说500~800w记录,所以大数据量建立索引是非常有必要的.My ...
- Job流程:决定map个数的因素
此文紧接Job流程:提交MR-Job过程.上一篇分析可以看出,MR-Job提交过程的核心代码在于 JobSubmitter 类的 submitJobInternal()方法.本文就由此方法的这一句代码 ...