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 ... 
随机推荐
- 二叉查找树(二叉排序树)的详细实现,以及随机平衡二叉查找树Treap的分析与应用
			这是一篇两年前写的东西,自我感觉还是相当不错的Treap教程.正好期末信息科学技术概论课要求交一个论文,就把这个东西修改了一下交了,顺便也发到这里吧. 随机平衡二叉查找树Treap的分析与应用 1.序 ... 
- BZOJ4268 : 小强的书架
			首先将所有高度乘上10,设f[i]为将前i本书放入书架的最小高度,则 \[\begin{eqnarray*}f[i]&=&\min(f[j-1]+first(j,i)+second(j ... 
- hdu 5828 Rikka with Sequence 线段树
			Rikka with Sequence 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5828 Description As we know, Rik ... 
- 如何利用JS判断当前来路域名并跳转到指定页面
			1.如何利用JS判断当前来路域名并跳转到指定页面 获取当前请求路径var href = location.href ;if(href.indexOf("baidu")>-1) ... 
- GitLab查询当前版本
			gitlab-rake gitlab:env:info 其实还有很多方法可以参考GitLab的帮助文档:https://docs.gitlab.com/omnibus/README.html 参考: ... 
- Anaconda、Miniconda、Conda、pip的相互关系_我是刘振岗_新浪博客
			Anaconda.Miniconda.Conda.pip的相互关系_我是刘振岗_新浪博客 http://blog.sina.com.cn/s/blog_8a122dcf0102x9vn.html 
- CodeSmith 基础用法和例子
			〇. 前言 一. 工具设置 CodeSmith默认是不支持中文的,那么我们必须要先设置使其支持中文显示,保存.并且要能够在生成文件中支持中文. [Tools ... 
- sqlserver 2012 IDE中 Windows身份验证连接服务器报错 ,Login failed for user 'xxx\Administrator'. 原因: 找不到与提供的名称匹配的登录名。
			问题描述: 本地装了两个实例,一个是SQLEXPRESS,可以正常操作.但是另一个开发常用的实例MSSQLSERVER却连Windows身份验证都报错,报的错误也是很奇葩,怎么会找不到Administ ... 
- C#打印图片
			打印的原理是:生成mdi文件,系统碰到mdi的时候会自动以打印的方式处理.所以,不管用什么模板,什么方式:能在PrintPage事件处理中,生成一张要打印内容的图片就OK了! C#实现打印源码如下: ... 
- linux 内核升级 转
			inux 内核升级 2011-03-25 23:13:28 分类: LINUX 因要测试一些软件,需要2.6.30以上的内核,安装好CentOS 5.5,内核是2.6.18-194.el5.这次的升级 ... 
