BlockingQueue的几个实现分析
ArrayBlockingQueue
底层以数组的结构存放队列元素,容量大小不可改变。
先看下变量:

items:数组,用于存放队列中的元素
takeIndex:获取元素的索引位置
putIndex:存放元素的索引位置
count:队列中当前元素数量
lock:控制队列进出的锁,ArrayBlockingQueue中进出共用一把锁,LinkedBlockingQueue中进出是两把分开的锁
notEmpty:从队列获取元素时的等待条件,也就是队列空时,获取元素的线程会阻塞,直到有线程放元素进来,notEmpty.signal唤醒
notFull:往队列里放元素时的等待条件,也就是队列满时,存放元素的线程会阻塞,直到有线程从队列里取元素并调用notFull.signal唤醒
下面看下代码更直观些:
put

1.先获得锁
2.循环判断当前元素数量是否等于数组长度,也就是队列的容量,等于的话就挂起当前线程,直到notFull.signal
3.插入元素到队列中,代码如下:

putIndex为下一次入队的索引位置,先把元素入队,然后+1后判断是否等于数组长度,如果等于就置位0,从0开始
最后count+1并notEmpty.signal唤醒因为队列为空导致获取元素阻塞的线程。
take

1.获取锁,注意,是和put同一把锁,也就是同时只能有一个线程获得操作队列的权限,无论是入队还是出队。
2.循环判断如果此时队空,就挂起当前线程,等待notEmpty.signal
3.出队:

获取元素并将数组中相应位置置空,设置下次入队索引并count-1,notFull.signal唤醒因为队满而阻塞的线程并返回出队元素
LinkedBlockingQueue
底层以单向链表结构存放队列元素,put和take分别采用两把不同的锁,也就是读写是并发运行的,
那么怎样控制并发环境下的出队和入队呢?答案就是使用AtomicInteger原子操作当前队列中元素数量。
下面看下有哪些实例变量:

capacity:队列容量,可以在构造方法中设置容量,如果不设置的话,默认为Integer.MAX_VALUE
count:当前队列中的元素数量,AtomicInteger类型,因为这里的put和take是两把锁,也就是会并发修改count,所以这里采用原子操作。
head:队头
last:队尾
takeLock:出队锁
notEmpty:出队时,如果队空,就等待notEmpty.signal
putLock:入队锁
notFull:入队时,如果队满,就等待notFull.signal
put
public void put(E e) throws InterruptedException {
     // 因为在出队时如果队空,有些方法是非阻塞或者等待一段时间后返回null的,比如poll
     // 所以禁止往队列中入队null
        if (e == null) throw new NullPointerException();
        // Note: convention in all put/take/etc is to preset local var
        // holding count negative to indicate failure unless set.
        int c = -1;
     // 将入队元素包装进链表的Node对象中
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
     // 获取入队锁
        putLock.lockInterruptibly();
        try {
            /*
             * Note that count is used in wait guard even though it is
             * not protected by lock. This works because count can
             * only decrease at this point (all other puts are shut
             * out by lock), and we (or some other waiting put) are
             * signalled if it ever changes from capacity. Similarly
             * for all other uses of count in other wait guards.
             */
       // 因为count加操作都在putLock中,所以这里可以是==判断
            while (count.get() == capacity) {
                notFull.await();
            }
       // 入队
            enqueue(node);
       // 获取原来count值并原子加一
            c = count.getAndIncrement();
       // 如果队列未满,通知阻塞线程继续入队
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
     // 如果队列之前是队空状态,说明之前有可能存在take元素的线程因为队空而阻塞,这里通知takeLock唤醒等待线程
        if (c == 0)
            signalNotEmpty();
    }

很简单,就是将刚刚的node链接到队尾
take

1.先获得takeLock
2.如果队空,挂起等待
3.出队
4.获取count值并减一
5.如果队列中还有元素,notEmpty唤醒之前因为队空阻塞的线程
6.如果take之前状态为队满,说明有可能存在因为队满而导致入队阻塞的线程,
唤醒他们(其实这里signal只唤醒一个,在被唤醒的线程完成入队后判断如果还有空间就继续signal,是这样一连串的动作)

出队逻辑如上,保持head的item为null,这里需要说明的是h.next = h自身循环引用也是会被GC的
SynchronousQueue
SynchronousQueue本身不存储元素,put元素时必须同步等待元素被取走。
PriorityBlockingQueue
具有优先级的队列
DelayQueue
具有延时的队列,延时不结束就不能取数据
BlockingQueue的几个实现分析的更多相关文章
- java线程池技术(一):ThreadFactory与BlockingQueue
		版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.ThreadFactory概述以及源码分析 ThreadFactory很简单,就是一个线程工厂也就是负责生产线程的,我们看下ThreadFact ... 
- Java多线程(六) —— 线程并发库之并发容器
		参考文献: http://www.blogjava.net/xylz/archive/2010/07/19/326527.html 一.ConcurrentMap API 从这一节开始正式进入并发容器 ... 
- 深入浅出 Java Concurrency (20): 并发容器 part 5 ConcurrentLinkedQueue[转]
		ConcurrentLinkedQueue是Queue的一个线程安全实现.先来看一段文档说明. 一个基于链接节点的无界线程安全队列.此队列按照 FIFO(先进先出)原则对元素进行排序.队列的头部 是队 ... 
- ArrayBlockingQueue,BlockingQueue分析
		BlockingQueue接口定义了一种阻塞的FIFO queue,每一个BlockingQueue都有一个容量,让容量满时往BlockingQueue中添加数据时会造成阻塞,当容量为空时取元素操作会 ... 
- Java并发包分析——BlockingQueue
		之前因为找实习的缘故,博客1个多月没有写了.找实习的经历总算告一段落,现在重新更新博客,这次的内容是分析Java并发包中的阻塞队列 关于阻塞队列,我之前是一直充满好奇,很好奇这个阻塞是怎么实现.现在我 ... 
- java并发包——阻塞队列BlockingQueue及源码分析
		一.摘要 BlockingQueue通常用于一个线程在生产对象,而另外一个线程在消费这些对象的场景,例如在线程池中,当运行的线程数目大于核心的线程数目时候,经常就会把新来的线程对象放到Blocking ... 
- java并发包分析之———BlockingQueue
		一.概述: BlockingQueue作为线程容器,可以为线程同步提供有力的保障. 二.BlockingQueue定义的常用方法 1.BlockingQueue定义的常用方法如下: 抛出异常 ... 
- JDK源码分析(11)之 BlockingQueue 相关
		本文将主要结合源码对 JDK 中的阻塞队列进行分析,并比较其各自的特点: 一.BlockingQueue 概述 说到阻塞队列想到的第一个应用场景可能就是生产者消费者模式了,如图所示: 根据上图所示,明 ... 
- 并发编程(九)—— Java 并发队列 BlockingQueue 实现之 LinkedBlockingQueue 源码分析
		LinkedBlockingQueue 在看源码之前,通过查询API发现对LinkedBlockingQueue特点的简单介绍: 1.LinkedBlockingQueue是一个由链表实现的有界队列阻 ... 
随机推荐
- 微信小程序UDP通信
			前言 UDP通信分为单播 广播 组播,基础库2.7.0之后,小程序开始支持UDP通信,目前小程序只支持单播. 小程序API 小程序UDP通信这一块可以说是很简单了就一个UDPSocket实例.然后bi ... 
- mybatis重新回顾
			此次在项目中相遇了mybatis,重新回顾下. 1.resulMap解决了结果集的列名字跟实体setter和getter不匹配的问题 其中property是实体的setter和getter对象,col ... 
- 设备驱动基础学习--/proc下增加节点
			在需要创建一个由一系列数据顺序组合而成的/proc虚拟文件或一个较大的/proc虚拟文件时,推荐使用seq_file接口. 数据结构struct seq_fille定义在include/linux/s ... 
- yii2关闭(开启)csrf的验证
			(1)全局使用,我们直接在配置文件中设置enableCookieValidation为true request => [ 'enableCookieValidation' => true, ... 
- C\C++改变鼠标样式
			改变鼠标样式可以使用SetClassLong函数 HCURSOR hcur = LoadCursor(NULL, IDC_CROSS); //加载系统自带鼠标样式 HWND hwnd = GetHWn ... 
- Aggregate 聚合用法
			var listb= ListA.Where(x => x.Id.Equals(obj.Id)).Select(x => x.SubData).Aggregate((x, y) => ... 
- Centos610-Nginx-TCP代理配置
			1.安装Nginx 详见<nginx>安装 2.下载nginx_tcp_proxy_module模块 下载 wget https://github.com/yaoweibin/nginx ... 
- 6_2 铁轨(UVa514)<栈>
			在一个叫「堆叠市」的城市中有一个有名的火车站.由于地形限制以及经费的关系,火车站及唯一的铁路的样子如下图: 现在火车从A方向来,预定从B方向离开.火车共有N节车厢(N <=1000),并且各车厢 ... 
- stream.js
			<script src='stream-min.js'></script> 下载 stream.js 2Kb minified streams是什么? Streams 是一个操 ... 
- BZOJ-1563-郁闷的出纳员(权值线段树)
			偏移量要考虑清楚. #include <bits/stdc++.h> using namespace std; const int N=4e5+10; const int BASE=1e5 ... 
