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的几个实现分析的更多相关文章

  1. java线程池技术(一):ThreadFactory与BlockingQueue

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.ThreadFactory概述以及源码分析 ThreadFactory很简单,就是一个线程工厂也就是负责生产线程的,我们看下ThreadFact ...

  2. Java多线程(六) —— 线程并发库之并发容器

    参考文献: http://www.blogjava.net/xylz/archive/2010/07/19/326527.html 一.ConcurrentMap API 从这一节开始正式进入并发容器 ...

  3. 深入浅出 Java Concurrency (20): 并发容器 part 5 ConcurrentLinkedQueue[转]

    ConcurrentLinkedQueue是Queue的一个线程安全实现.先来看一段文档说明. 一个基于链接节点的无界线程安全队列.此队列按照 FIFO(先进先出)原则对元素进行排序.队列的头部 是队 ...

  4. ArrayBlockingQueue,BlockingQueue分析

    BlockingQueue接口定义了一种阻塞的FIFO queue,每一个BlockingQueue都有一个容量,让容量满时往BlockingQueue中添加数据时会造成阻塞,当容量为空时取元素操作会 ...

  5. Java并发包分析——BlockingQueue

    之前因为找实习的缘故,博客1个多月没有写了.找实习的经历总算告一段落,现在重新更新博客,这次的内容是分析Java并发包中的阻塞队列 关于阻塞队列,我之前是一直充满好奇,很好奇这个阻塞是怎么实现.现在我 ...

  6. java并发包——阻塞队列BlockingQueue及源码分析

    一.摘要 BlockingQueue通常用于一个线程在生产对象,而另外一个线程在消费这些对象的场景,例如在线程池中,当运行的线程数目大于核心的线程数目时候,经常就会把新来的线程对象放到Blocking ...

  7. java并发包分析之———BlockingQueue

    一.概述: BlockingQueue作为线程容器,可以为线程同步提供有力的保障.   二.BlockingQueue定义的常用方法 1.BlockingQueue定义的常用方法如下:   抛出异常 ...

  8. JDK源码分析(11)之 BlockingQueue 相关

    本文将主要结合源码对 JDK 中的阻塞队列进行分析,并比较其各自的特点: 一.BlockingQueue 概述 说到阻塞队列想到的第一个应用场景可能就是生产者消费者模式了,如图所示: 根据上图所示,明 ...

  9. 并发编程(九)—— Java 并发队列 BlockingQueue 实现之 LinkedBlockingQueue 源码分析

    LinkedBlockingQueue 在看源码之前,通过查询API发现对LinkedBlockingQueue特点的简单介绍: 1.LinkedBlockingQueue是一个由链表实现的有界队列阻 ...

随机推荐

  1. 微信小程序UDP通信

    前言 UDP通信分为单播 广播 组播,基础库2.7.0之后,小程序开始支持UDP通信,目前小程序只支持单播. 小程序API 小程序UDP通信这一块可以说是很简单了就一个UDPSocket实例.然后bi ...

  2. mybatis重新回顾

    此次在项目中相遇了mybatis,重新回顾下. 1.resulMap解决了结果集的列名字跟实体setter和getter不匹配的问题 其中property是实体的setter和getter对象,col ...

  3. 设备驱动基础学习--/proc下增加节点

    在需要创建一个由一系列数据顺序组合而成的/proc虚拟文件或一个较大的/proc虚拟文件时,推荐使用seq_file接口. 数据结构struct seq_fille定义在include/linux/s ...

  4. yii2关闭(开启)csrf的验证

    (1)全局使用,我们直接在配置文件中设置enableCookieValidation为true request => [ 'enableCookieValidation' => true, ...

  5. C\C++改变鼠标样式

    改变鼠标样式可以使用SetClassLong函数 HCURSOR hcur = LoadCursor(NULL, IDC_CROSS); //加载系统自带鼠标样式 HWND hwnd = GetHWn ...

  6. Aggregate 聚合用法

    var listb= ListA.Where(x => x.Id.Equals(obj.Id)).Select(x => x.SubData).Aggregate((x, y) => ...

  7. Centos610-Nginx-TCP代理配置

    1.安装Nginx 详见<nginx>安装 2.下载nginx_tcp_proxy_module模块 下载  wget https://github.com/yaoweibin/nginx ...

  8. 6_2 铁轨(UVa514)<栈>

    在一个叫「堆叠市」的城市中有一个有名的火车站.由于地形限制以及经费的关系,火车站及唯一的铁路的样子如下图: 现在火车从A方向来,预定从B方向离开.火车共有N节车厢(N <=1000),并且各车厢 ...

  9. stream.js

    <script src='stream-min.js'></script> 下载 stream.js 2Kb minified streams是什么? Streams 是一个操 ...

  10. BZOJ-1563-郁闷的出纳员(权值线段树)

    偏移量要考虑清楚. #include <bits/stdc++.h> using namespace std; const int N=4e5+10; const int BASE=1e5 ...