ArrayBlockingQueue, LinkedBlockingQueue, ConcurrentLinkedQueue, RingBuffer
1. ArrayBlockingQueue, LinkedBlockingQueue, ConcurrentLinkedQueue
ArrayBlockingQueue, LinkedBlockingQueue 继承自 BlockingQueue, 他们的特点就是 Blocking, Blocking 特有的方法就是 take() 和 put(), 这两个方法是阻塞方法, 每当队列容量满的时候, put() 方法就会进入wait, 直到队列空出来, 而每当队列为空时, take() 就会进入等待, 直到队列有元素可以 take()
ArrayBlockingQueue, LinkedBlockingQueue 区别在于 ArrayBlockingQueue 必须指定容量, 且可以指定 fair 变量, 如果 fair 为 true, 则会保持 take() 或者 put() 操作时线程的 block 顺序, 先 block 的线程先 take() 或 put(), fair 又内部变量 ReentrantLock 保证
ConcurrentLinkedQueue 通过 CAS 操作实现了无锁的 poll() 和 offer(), 他的容量是动态的, 由于无锁, 所以在 poll() 或者 offer() 的时候 head 与 tail 可能会改变, 所以它会持续的判断 head 与 tail 是否改变来保证操作正确性, 如果改变, 则会重新选择 head 与 tail. 而由于无锁的特性, 他的元素更新与 size 变量更新无法做到原子 (实际上它没有 size 变量), 所以他的 size() 是通过遍历 queue 来获得的, 在效率上是 O(n), 而且无法保证准确性, 因为遍历的时候有可能 queue size 发生了改变.
RingBuffer 是 Distruptor 中的一个用来替代 ArrayBlockingQueue 的队列, 它的思想在于长度可控, 且无锁, 只有在 blocking 的时候(没有数据的时候出队, 数据满的时候入队)会自旋. 实现原理是使用一个环形array, 生产者作为 tail, 消费者作为 head, 每生产一次 tail atomic++, 每消费一次 head atomic++, tail 不能超过 head 一圈(array size, 即队列满时 blocking), tail 不能超过自己tail一圈(即不能覆盖未被消费的值), head 不能超过 tail (即无可消费任务时 blocking), head 不能取到空值(取到空值时 blocking). blocking 使用一个 while 自旋来完成, 那么只要生产者消费者的速度相当时, 即可通过 atomicInteger(cas) 保证无锁, 而如果你需要在 blocking 的时候立即返回, 则 while 自旋都可以不需要. 相比于 ArrayBlockingQueue, 它可以绝大部分时间无锁, blocking 自旋, 相比于 concurrentLinkedQueue, 他又能做到长度限制. 代码如下:
public class RingBuffer<T> implements Serializable { /**
*
*/
private static final long serialVersionUID = 6976960108708949038L; private volatile AtomicInteger head; private volatile AtomicInteger tail; private int length; final T EMPTY = null; private volatile T[] queue; public RingBuffer(Class<T> type, int length){
this.head = new AtomicInteger(0);
this.tail = new AtomicInteger(0);
this.length = length == 0 ? 2 << 16 : length; // 默认2^16
this.queue = (T[]) Array.newInstance(type, this.length);
} public void enQueue(T t){
if(t == null) t= (T) new Object();
// 阻塞 -- 避免多生成者循环生产同一个节点
while(this.getTail() - this.getHead() >= this.length);
int ctail = this.tail.getAndIncrement();
while(this.queue[this.getTail(ctail)] != EMPTY); // 自旋
this.queue[this.getTail(ctail)] = t;
} public T deQueue(){
T t = null;
// 阻塞 -- 避免多消费者循环消费同一个节点
while(this.head.get() >= this.tail.get());
int chead = this.head.getAndIncrement();
while(this.queue[this.getHead(chead)] == EMPTY); // 自旋
t = this.queue[this.getHead(chead)];
this.queue[this.getHead(chead)] = EMPTY;
return t;
} public int getHead(int index){
return index & (this.length - 1);
} public int getTail(int index) {
return index & (this.length - 1);
} public int getHead() {
return head.get() & (this.length - 1);
} public int getTail() {
return tail.get() & (this.length - 1);
} public T[] getQueue() {
return queue;
} public int getLength() {
return length;
} public void setLength(int length) {
this.length = length;
} }
ArrayBlockingQueue, LinkedBlockingQueue, ConcurrentLinkedQueue, RingBuffer的更多相关文章
- 高并发第十三弹:J.U.C 队列 SynchronousQueue.ArrayBlockingQueue.LinkedBlockingQueue.LinkedTransferQueue
因为下一节会说线程池,要用线程池 那么线程池有个很重要的参数 就是Queue的选择 常用的队列其实就两种: 先进先出(FIFO):先插入的队列的元素也最先出队列,类似于排队的功能.从某种程度上来说这种 ...
- LinkedBlockingQueue与ArrayBlockingQueue
阻塞队列与普通的队列(LinkedList/ArrayList)相比,支持在向队列中添加元素时,队列的长度已满阻塞当前添加线程,直到队列未满或者等待超时:从队列中获取元素时,队列中元素为空 ,会将获取 ...
- LinkedBlockingQueue 和 ConcurrentLinkedQueue的区别
1. 简单的开篇 LinkedBlockingQueue 和 ConcurrentLinkedQueue 是 Java 高并发场景中最常使用的队列.尽管这两个队列经常被用作并发场景的数据结构,但它们之 ...
- Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍
1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...
- 五种并发包总结ConcurrentHashMap CopyOnWriteArrayList ArrayblockingQueue
五种并发包总结 1.常用的五种并发包 ConcurrentHashMap CopyOnWriteArrayList CopyOnWriteArraySet ArrayBlockingQueue Lin ...
- java多线程系类:JUC集合:01之框架
概要 之前,在"Java 集合系列目录(Category)"中,讲解了Java集合包中的各个类.接下来,将展开对JUC包中的集合进行学习.在学习之前,先温习一下"Java ...
- Java多线程系列--“JUC集合”01之 框架
概要 之前,在"Java 集合系列目录(Category)"中,讲解了Java集合包中的各个类.接下来,将展开对JUC包中的集合进行学习.在学习之前,先温习一下"Java ...
- Java集合容器简介
Java集合容器主要有以下几类: 1,内置容器:数组 2,list容器:Vetor,Stack,ArrayList,LinkedList, CopyOnWriteArrayList(1.5),Attr ...
- Java队列集合的性能测试
同时开10个线程存入和取出100万的数据,结论如下: DoubleBufferedQueue < ConcurrentLinkedQueue < ArrayBlockingQueue &l ...
随机推荐
- 实现C语言字符串操作的库函数 包括基本的字符串复制 字符串长度 字符串比较等多种函数(C代码)
头文件 "mystring.h" #ifndef _MYSTR_H #define _MYSTR_H #include <stdio.h> #include <s ...
- 如何使用DSP的cache(转)
C6747在执行一块算法的执行时间在114ms左右,需求要20ms以下.6000属于分层存储器体系架构,内部RAM跟CPU不同频运行,只有cache使能才跟CPU同频.可能是cache没打开.下面转载 ...
- poj-2421-最小生成树刷题
title: poj-2421-最小生成树刷题 date: 2018-11-20 20:30:29 tags: acm 刷题 categories: ACM-最小生成树 概述 做了几道最小生成树的题, ...
- html (第四本书第八章参考)
上机1 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8 ...
- java集合之vector容器
学完ArrayList和LinkedList之后,我们接着学习Vector.第1部分 Vector介绍第2部分 Vector数据结构第3部分 Vector源码解析(基于JDK1.6.0_45)第4部分 ...
- Windows 7重启后USB 3.0无法使用的问题解决
1.首先对主板USB3.0驱动程序进行重新安装 2.如果驱动程序重装后还是无法解决无法使用USB3.0设备的话,在win7桌面上找到“计算机”图标并鼠标右键,选择“管理”选项,找到设备管理器,然后找到 ...
- STM32 TIMER REGISTER
- LPC43xx SGPIO Slice 输入输出连接表
- echarts 怎样去掉白色边框线 和怎样去除背景中的网格
echarts怎样去掉白色边框线: echarts怎样去除背景中的网格线
- epoll的LT和ET使用EPOLLONESHOT
epoll有两种触发的方式即LT(水平触发)和ET(边缘触发)两种,在前者,只要存在着事件就会不断的触发,直到处理完成,而后者只触发一次相同事件或者说只在从非触发到触发两个状态转换的时候儿才触发. 这 ...