java阻塞队列与非阻塞队列
在并发编程中,有时候需要使用线程安全的队列。如果要实现一个线程安全的队列有两种方式:一种是使用阻塞算法,另一种是使用非阻塞算法。
//使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现。非阻塞的实现方式则可以使用循环CAS的方式来实现。
阻塞队列:
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法。
1)支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。
2)支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空。
阻塞队列常用于生产者和消费者的场景,生产者是向队列里添加元素的线程,消费者是从队列里取元素的线程。阻塞队列就是生产者用来存放元素、消费者用来获取元素的容器。
下表是阻塞队列的部分方法:
方法\处理方式 | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
---|---|---|---|---|
插入方法 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
移除方法 | remove() | poll() | take() | poll(time,unit) |
检查方法 | element() | peek() | 不可用 | 不可用 |
- 抛出异常:是指当阻塞队列满时候,再往队列里插入元素,会抛出IllegalStateException(“Queue full”)异常。当队列为空时,从队列里获取元素时会抛出NoSuchElementException异常 。
- 返回特殊值:插入方法会返回是否成功,成功则返回true。移除方法,则是从队列里拿出一个元素,如果没有则返回null
- 一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。当队列空时,消费者线程试图从队列里take元素,队列也会阻塞消费者线程,直到队列可用。
- 超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。
JDK7提供了7个阻塞队列。分别是:
- ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
- PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
- DelayQueue:一个使用优先级队列实现的无界阻塞队列。
- SynchronousQueue:一个不存储元素的阻塞队列。
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
简单介绍下其中三个队列:
SynchronousQueue
SynchronousQueue是无界的,是一种无缓冲的等待队列,但是由于该Queue本身的特性,在某次添加元素后必须等待其他线程取走后才能继续添加;可以认为SynchronousQueue是一个缓存值为1的阻塞队列,但是
isEmpty()方法永远返回是true,remainingCapacity() 方法永远返回是0,remove()和removeAll()
方法永远返回是false,iterator()方法永远返回空,peek()方法永远返回null。
声明一个SynchronousQueue有两种不同的方式,它们之间有着不太一样的行为。公平模式和非公平模式的区别:如果采用公平模式:SynchronousQueue会采用公平锁,并配合一个FIFO队列来阻塞多余的生产者和消费者,从而体系整体的公平策略;但如果是非公平模式(SynchronousQueue默认):SynchronousQueue采用非公平锁,同时配合一个LIFO队列来管理多余的生产者和消费者,而后一种模式,如果生产者和消费者的处理速度有差距,则很容易出现饥渴的情况,即可能有某些生产者或者是消费者的数据永远都得不到处理。
LinkedBlockingQueue
LinkedBlockingQueue默认大小是Integer.MAX_VALUE,可以设定大小,可以理解为一个缓存的有界等待队列。
基于链表的阻塞队列,内部维持着一个数据缓冲队列(该队列由链表构成)。当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。
LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。
ArrayListBlockingQueue
ArrayListBlockingQueue是有界的,是一个有界缓存的等待队列。
基于数组的阻塞队列,同LinkedBlockingQueue类似,内部维持着一个定长数据缓冲队列(该队列由数组构成)。ArrayBlockingQueue内部还保存着两个整形变量,分别标识着队列的头部和尾部在数组中的位置。
ArrayBlockingQueue在生产者放入数据和消费者获取数据,都是共用同一个锁对象,由此也意味着两者无法真正并行运行,这点尤其不同于LinkedBlockingQueue;按照实现原理来分析,ArrayBlockingQueue完全可以采用分离锁,从而实现生产者和消费者操作的完全并行运行。Doug
Lea之所以没这样去做,也许是因为ArrayBlockingQueue的数据写入和获取操作已经足够轻巧,以至于引入独立的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜。
ArrayBlockingQueue和LinkedBlockingQueue间还有一个明显的不同之处在于,前者在插入或删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的Node对象。这在长时间内需要高效并发地处理大批量数据的系统中,其对于GC的影响还是存在一定的区别。
非阻塞队列:
ConcurrentLinkedQueue
ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部;当我们获取一个元素时,它会返回队列头部的元素。
结构如下:
- public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
- implements Queue<E>, java.io.Serializable {
- private transient volatile Node<E> head;//头指针
- private transient volatile Node<E> tail;//尾指针
- public ConcurrentLinkedQueue() {//初始化,head=tail=(一个空的头结点)
- head = tail = new Node<E>(null);
- }
- private static class Node<E> {
- volatile E item;
- volatile Node<E> next;//内部是使用单向链表实现
- ......
- }
- ......
- }
入队和出队操作均利用CAS(compare and set)更新,这样允许多个线程并发执行,并且不会因为加锁而阻塞线程,使得并发性能更好。
java阻塞队列与非阻塞队列的更多相关文章
- 【IBM】Merlin 给 Java 平台带来了非阻塞 I/O
Merlin 给 Java 平台带来了非阻塞 I/O 新增的功能大幅降低了线程开销 Java 技术平台早就应该提供非阻塞 I/O 机制了.幸运的是,Merlin(JDK 1.4)有一根几乎在各个场合都 ...
- Java基础知识强化之多线程笔记07:同步、异步、阻塞式、非阻塞式 的联系与区别
1. 同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.但是一旦调用返回,就必须先得到返回值了. 换句话话说,调用者主动等待这个"调用"的结果. 对于 ...
- 简单测试Java线程安全中阻塞同步与非阻塞同步性能
摘抄自周志明老师的<深入理解Java虚拟机:JVM高级特性与最佳实践>13.2.2 线程安全的实现方法 1.名词解释 同步是指锁哥线程并发访问共享数据时,保证共享数据同一时刻只被一个线程访 ...
- java网络通信:异步非阻塞I/O (NIO)
转: java网络通信:异步非阻塞I/O (NIO) 首先是channel,是一个双向的全双工的通道,可同时读写,而输入输出流都是单工的,要么读要么写.Channel分为两大类,分别是用于网络数据的S ...
- 如何解读 Java IO、NIO 中的同步阻塞与同步非阻塞?
原文链接:如何解读 Java IO.NIO 中的同步阻塞与同步非阻塞? 一.前言 最近刚读完一本书:<Netty.Zookeeper.Redis 并发实战>,个人觉得 Netty 部分是写 ...
- 阻塞式和非阻塞式IO
有很多人把阻塞认为是同步,把非阻塞认为是异步:个人认为这样是不准确的,当然从思想上可以这样类比,但方式是完全不同的,下面说说在JAVA里面阻塞IO和非阻塞IO的区别 在JDK1.4中引入了一个NIO的 ...
- 网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO
同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问题其实不同的人给出 ...
- 转 网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO
此文章为转载,如有侵权,请联系本人.转载出处,http://blog.chinaunix.net/uid-28458801-id-4464639.html 同步(synchronous) IO和异步( ...
- 理论铺垫:阻塞IO、非阻塞IO、IO多路复用/事件驱动IO(单线程高并发原理)、异步IO
完全来自:http://www.cnblogs.com/alex3714/articles/5876749.html 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同 ...
- 事件驱动模型 IO多路复用 阻塞IO与非阻塞IO select epool
一.事件驱动 1.要理解事件驱动和程序,就需要与非事件驱动的程序进行比较.实际上,现代的程序大多是事件驱动的,比如多线程的程序,肯定是事件驱动的.早期则存在许多非事件驱动的程序,这样的程序,在需要等待 ...
随机推荐
- 完善_IO, _IOR, _IOW, _IOWR 宏的用法与解析
_IO, _IOR, _IOW, _IOWR 宏的用法与解析 原文地址:http://www.eefocus.com/ayayayaya/blog/12-03/245777_20cdd.html 作 ...
- mysql5.7创建用户授权删除用户撤销授权
一, 创建用户: 命令: CREATE USER 'username'@'host' IDENTIFIED BY 'password'; 说明:username - 你将创建的用户名, host - ...
- Redis学习之路(005)- redis内存数据库C客户端hiredis API 中文说明
A)编译安装 make make install (/usr/local) make install PREFIX=$HOME/progs(可以自由指定安装路径) B)同步的API接口 redisCo ...
- WCF Service中HttpContext.Current为null的解决办法
1. 在hosting WCF的web.config中加入: <system.serviceModel> <serviceHostingEnvironment aspNetCompa ...
- java 发送post请求参数中含有+会转化为空格的问题
如题 原因分析:参数在传递过程中经历的几次编码和解码标准不同,导致加号.空格等字符的错误. 解决方案:将post请求的参数中 ,含有+号的,统统采用%2B 去替换,这是URL的协议问题.
- 获取*.jks签名的方法(Android studio)
- mvc 权限验证
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...
- “21天教你学会C++”
下面是一个<Teach Yourself C++ in 21 Days>的流程图,请各位程序员同仁认真领会.如果有必要,你可以查看这个图书以作参照:http://www.china-pu ...
- spark.mllib源代码阅读-优化算法1-Gradient
Spark中定义的损失函数及梯度,在看源代码之前,先回想一下机器学习中定义了哪些损失函数,毕竟梯度求解是为优化求解损失函数服务的. 监督学习问题是在如果空间F中选取模型f作为决策函数.对于给定的输入X ...
- 并发测试 JavaDemo
https://github.com/oldratlee/fucking-java-concurrency /** * @author Jerry Lee */ public class Testee ...