ArrayBlockingQueue

ArrayBlockingQueue 能解决什么问题?什么时候使用 ArrayBlockingQueue?

1)ArrayBlockingQueue 是底层由数组支持的有界阻塞队列,队列按照 FIFO 顺序对元素进行排序,读取元素从头部开始,写入元素追加到队列尾部。
2)当容量超出限制时,put 写入操作将被阻塞;当队列为空时,take 读取操作将被阻塞。
3)offer 操作支持 fast-fail 写入和超时写入,poll 支持 fast-fail 读取和超时读取。
4)ArrayBlockingQueue 支持对等待的生产者线程和消费者线程进行排序的可选公平策略,默认情况下是非公平的,公平模式下会降低其吞吐量。
5)ArrayBlockingQueue 使用 ReentrantLock 来保证线程安全。

如何使用 ArrayBlockingQueue?

1)生成者消费者并发读写的场景下,并且生产者和消费者基本平衡。

使用 ArrayBlockingQueue 有什么风险?

1)读写操作使用相同的互斥锁,不支持并发读写,相对于 LinkedBlockingQueue 而言性能不是特别高。
2)消费者和生产者不平衡时,高并发读写会阻塞操作线程导致大量线程等待,浪费资源。

ArrayBlockingQueue 核心操作的实现原理?

  • 创建实例
    /** 底层存储元素的对象数组 */
final Object[] items; /** 下一次 take, poll, peek or remove 操作的目标元素索引 */
int takeIndex; /** 下一次 put, offer, or add 操作的目标元素索引*/
int putIndex; /** 队列中的已有元素个数 */
int count; /*
* Concurrency control uses the classic two-condition algorithm
* found in any textbook.
*/ /** 保证线程安全访问的可重入互斥锁 */
final ReentrantLock lock; /** 读取元素时,队列为空,则在该条件上阻塞 */
private final Condition notEmpty; /** 写入元素时,队列已满,则在该条件上阻塞 */
private final Condition notFull; /**
* 创建最大容量为 capacity 的非公平有界阻塞队列。
*/
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
} /**
* 1)fair=true,创建最大容量为 capacity 的公平有界阻塞队列。
* 2)fair=true,创建最大容量为 capacity 的非公平有界阻塞队列。
*/
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0) {
throw new IllegalArgumentException();
}
this.items = new Object[capacity];
// 通过 ReentrantLock 来保证多线程并发安全性
lock = new ReentrantLock(fair);
// 读取元素时队列为空,则在非空添加上阻塞等待
notEmpty = lock.newCondition();
// 写入元素时队列已满,则在非满条件上阻塞等待
notFull = lock.newCondition();
}
  • 写入元素:获取互斥锁,进入条件队列后释放互斥锁,节点被转移到同步队列中并获取互斥锁,put 操作完毕释放互斥锁。
    /**
* 将元素添加到队列尾部,如果队列已满,则阻塞当前线程。
* 可响应线程中断
*/
@Override
public void put(E e) throws InterruptedException {
Objects.requireNonNull(e);
// 读取互斥锁
final ReentrantLock lock = this.lock;
// 可中断地获取互斥锁
lock.lockInterruptibly();
try {
// 如果队列已满
while (count == items.length) {
/**
* 则将当前线程加入非满的条件队列,并阻塞等待唤醒,
* 当前线程被唤醒后会再次尝试将目标元素加入到队列中,可以被重复阻塞。
*/
notFull.await();
}
// 将元素添加到队列尾部
enqueue(e);
} finally {
// 释放锁
lock.unlock();
}
} AbstractQueuedSynchronizer#
/**
* 当前线程在指定条件上阻塞等待
*/
public final void await() throws InterruptedException {
// 线程被设置了中断标识
if (Thread.interrupted()) {
throw new InterruptedException();
}
// 创建一个新的节点并将其加入到条件队列尾部
final Node node = addConditionWaiter();
// 尝试释放互斥锁,并返回同步状态
final int savedState = fullyRelease(node);
int interruptMode = 0;
// 如果新建节点在条件队列中
while (!isOnSyncQueue(node)) {
// 阻塞当前线程,等待被 signal 唤醒或被其他线程中断
LockSupport.park(this);
// 读取线程的中断状态,如果未被中断,则退出循环
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) {
break;
}
}
/**
* 1)在独占、线程不可中断的模式下获取锁,如果线程被中断
* && 是在被唤醒后中断,则更新中断模式。
*/
if (acquireQueued(node, savedState) && interruptMode != ConditionObject.THROW_IE) {
interruptMode = ConditionObject.REINTERRUPT;
}
// 当前节点存在后置节点
if (node.nextWaiter != null) {
// 踢除无效节点
unlinkCancelledWaiters();
}
// 如果中断模式不是 0
if (interruptMode != 0) {
reportInterruptAfterWait(interruptMode);
}
} private Node addConditionWaiter() {
// 互斥锁没有被当前线程持有
if (!isHeldExclusively()) {
// 则抛出 IllegalMonitorStateException 异常
throw new IllegalMonitorStateException();
}
// 读取最后一个等待节点
Node t = lastWaiter;
// 条件队列中的最后一个节点已经被取消
if (t != null && t.waitStatus != Node.CONDITION) {
// 踢除所有被取消的节点
unlinkCancelledWaiters();
// 重新读取条件队列尾节点
t = lastWaiter;
}
// 创建一个条件节点,waitStatus=-2
final Node node = new Node(Node.CONDITION);
// 尾节点为空,表示当前节点是第一个有效的节点
if (t == null) {
// 设置头节点
firstWaiter = node;
} else {
// 更新旧尾节点的后置节点,即将当前节点链接到条件队列尾部
t.nextWaiter = node;
}
// 更新尾节点
lastWaiter = node;
return node;
} private void unlinkCancelledWaiters() {
// 读取条件队列头节点
Node t = firstWaiter;
Node trail = null;
while (t != null) {
// 读取后置节点
final Node next = t.nextWaiter;
// 当前节点的同步状态不是 Node.CONDITION,则需要踢除
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;
// 还没有有效的节点
if (trail == null) {
// 更新头节点
firstWaiter = next;
} else {
// 更新最近一个有效节点的 nextWaiter
trail.nextWaiter = next;
}
// 已经遍历到最后一个节点,并且该节点被取消
if (next == null) {
// 更新尾节点为最近一个有效节点
lastWaiter = trail;
}
} else {
// 写入最近一个有效节点
trail = t;
}
// 递归处理下一个节点
t = next;
}
} /**
* 释放互斥锁
*/
final int fullyRelease(Node node) {
try {
// 读取同步状态
final int savedState = getState();
// 在独占模式下释放锁
if (release(savedState)) {
return savedState;
}
throw new IllegalMonitorStateException();
} catch (final Throwable t) {
node.waitStatus = Node.CANCELLED;
throw t;
}
} /**
* 在独占模式下释放锁
*/
public final boolean release(int arg) {
// 尝试释放锁
if (tryRelease(arg)) {
// 读取头节点
final Node h = head;
// 头结点的同步状态不为 0,则唤醒其后置节点
if (h != null && h.waitStatus != 0) {
// 唤醒目标节点的后继节点
unparkSuccessor(h);
}
return true;
}
return false;
} /**
* 形参节点是否在条件队列中
*/
final boolean isOnSyncQueue(Node node) {
// 节点等待状态为 Node.CONDITION,或节点无前置节点,都说明其在同步队列中
if (node.waitStatus == Node.CONDITION || node.prev == null) {
return false;
}
// 节点的 next 不为 null,则其在条件队列中
if (node.next != null) {
return true;
}
// 从尾部开始查找节点
return findNodeFromTail(node);
} /**
* 从尾部向前查找形参节点,如果其在条件队列中,则返回 true
*/
private boolean findNodeFromTail(Node node) {
for (Node p = tail;;) {
// 当前节点就是目标节点
if (p == node) {
return true;
}
// 已经无前置节点
if (p == null) {
return false;
}
// 迭代前一个节点
p = p.prev;
}
} /**
* 检查线程中断状态
* 1)ConditionObject.THROW_IE 线程在被唤醒前,被其他线程中断
* 2)ConditionObject.REINTERRUPT 线程在被唤醒后,被其他线程中断
* 3)0 线程未被中断
*/
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
transferAfterCancelledWait(node) ? ConditionObject.THROW_IE : ConditionObject.REINTERRUPT :
0;
} /**
* 等待条件已经取消,则尝试将当前节点转移到同步队列
*/
final boolean transferAfterCancelledWait(Node node) {
// 更新同步状态为 0
if (node.compareAndSetWaitStatus(Node.CONDITION, 0)) {
// 将当前节点加入到同步队列尾部,等待获取锁
enq(node);
return true;
}
/**
* 在节点被转移到同步队列时,线程被唤醒,则自旋等待其完全进入同步队列为止
*/
while (!isOnSyncQueue(node)) {
Thread.yield();
}
return false;
} /**
* 将节点加入到同步队列尾部
*/
private Node enq(Node node) {
for (;;) {
final Node oldTail = tail;
if (oldTail != null) {
node.setPrevRelaxed(oldTail);
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return oldTail;
}
} else {
initializeSyncQueue();
}
}
}
/**
* 在独占、线程不可中断的模式下获取锁,当前线程已经在同步队列中。
*/
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
for (;;) {
// 读取当前节点的前置节点
final Node p = node.predecessor();
// 如果前置节点是 head,则尝试获取锁
if (p == head && tryAcquire(arg)) {
// 获取成功,则设置当前节点为 head
setHead(node);
p.next = null; // help GC
// 返回线程中断标识
return interrupted;
}
// 当前线程是否需要被阻塞
if (AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire(p, node)) {
// 阻塞当前线程,并等待唤醒,唤醒后返回其中断状态
interrupted |= parkAndCheckInterrupt();
}
}
// 线程运行过程中出现异常
} catch (final Throwable t) {
// 取消当前节点
cancelAcquire(node);
// 如果线程被设置中断标识
if (interrupted) {
// 则线程自我中断
AbstractQueuedSynchronizer.selfInterrupt();
}
throw t;
}
} private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
// 1)线程在被唤醒前,被其他线程设置了中断标识
if (interruptMode == ConditionObject.THROW_IE) {
// 则抛出 InterruptedException 异常
throw new InterruptedException();
// 2)线程在被唤醒后,被其他线程设置了中断标识
} else if (interruptMode == ConditionObject.REINTERRUPT) {
// 中断当前线程
AbstractQueuedSynchronizer.selfInterrupt();
}
} private void enqueue(E e) {
// 读取底层对象数组
final Object[] items = this.items;
// 插入元素
items[putIndex] = e;
// 递增下一个插入索引,如果已经到达数组长度
if (++putIndex == items.length) {
// 循环更新到数组头部
putIndex = 0;
}
// 递增元素总数
count++;
// 唤醒在非空条件上阻塞等待的单个线程
notEmpty.signal();
} AbstractQueuedSynchronizer#
/**
* 唤醒在当前条件上阻塞等待的单个线程
*/
public final void signal() {
// 互斥锁没有被当前线程持有
if (!isHeldExclusively()) {
// 则抛出 IllegalMonitorStateException异常
throw new IllegalMonitorStateException();
}
// 读取条件队列中第一个等待的节点
final Node first = firstWaiter;
if (first != null) {
// 将该节点从条件队列转移到同步队列
doSignal(first);
}
} /**
* 将目标节点从条件队列转移到同步队列中
*/
private void doSignal(Node first) {
do {
// 更新 firstWaiter 为当前节点的后置节点
if ( (firstWaiter = first.nextWaiter) == null) {
// 如果无后置节点
lastWaiter = null;
}
first.nextWaiter = null;
// 转移成功则退出循环
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
} /**
* 将形参节点从条件队列转移到同步队列,转移成功返回 true
*/
final boolean transferForSignal(Node node) {
/**
* If cannot change waitStatus, the node has been cancelled.
* 节点在转移前被取消
*/
if (!node.compareAndSetWaitStatus(Node.CONDITION, 0)) {
return false;
} // 将节点加入到同步队列尾部
final Node p = enq(node);
// 读取其同步状态
final int ws = p.waitStatus;
/**
* 1)转移成功后线程被中断
* 2)原子更新状态失败,即节点被取消
*/
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL)) {
// 唤醒驻留其上的线程
LockSupport.unpark(node.thread);
}
return true;
}
  • 读取元素
    @Override
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
// 队列为空
while (count == 0) {
// 则在非空条件上阻塞等待
notEmpty.await();
}
return dequeue();
} finally {
lock.unlock();
}
} /**
* 移除并返回队列头部元素
*/
private E dequeue() {
final Object[] items = this.items;
final
// 读取目标元素
E e = (E) items[takeIndex];
// 将目标索引处的元素置空
items[takeIndex] = null;
// 循环递增读取索引
if (++takeIndex == items.length) {
takeIndex = 0;
}
count--;
if (itrs != null) {
itrs.elementDequeued();
}
// 唤醒在非满条件上等待的线程
notFull.signal();
return e;
}
  • 非阻塞添加元素
    /**
* 1)如果队列已满,则返回 false,元素添加失败。
* 2)如果队列有可用空间,则添加元素,并返回 true。
*/
@Override
public boolean offer(E e) {
Objects.requireNonNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 1)队列已满
if (count == items.length) {
return false;
// 2)队列中有可用位置
} else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
} /**
* 1)如果队列有可用位置,则添加元素到队列尾部,并返回 true。
* 2)如果队列已满,在指定的超时时间内队列可用,则添加元素;否则,返回 false,添加失败。
*/
@Override
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException { Objects.requireNonNull(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) {
if (nanos <= 0L) {
return false;
}
// 在非满条件上等待指定的超时时间
nanos = notFull.awaitNanos(nanos);
}
enqueue(e);
return true;
} finally {
lock.unlock();
}
} AbstractQueuedSynchronizer#awaitNanos
/**
* 1)当前线程被设置了中断标识,则抛出 InterruptedException 异常。
* 2)阻塞直到当前线程被唤醒、中断、或超时。
*/
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
// 如果线程已经被设置了中断标识,则抛出 InterruptedException 异常
if (Thread.interrupted()) {
throw new InterruptedException();
}
// 计算截止时间
final long deadline = System.nanoTime() + nanosTimeout;
final long initialNanos = nanosTimeout;
// 往条件队列中添加节点
final Node node = addConditionWaiter();
// 尝试释放互斥锁
final int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
// 如果已经超时
if (nanosTimeout <= 0L) {
// 将节点从条件队列转移到同步队列中
transferAfterCancelledWait(node);
break;
}
// 如果超时时间 > 1000 纳秒
if (nanosTimeout > AbstractQueuedSynchronizer.SPIN_FOR_TIMEOUT_THRESHOLD) {
// 最多阻塞当期线程指定的超时时间
LockSupport.parkNanos(this, nanosTimeout);
}
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) {
break;
}
// 计算剩余时间
nanosTimeout = deadline - System.nanoTime();
}
// 尝试获取互斥锁
if (acquireQueued(node, savedState) && interruptMode != ConditionObject.THROW_IE) {
interruptMode = ConditionObject.REINTERRUPT;
}
if (node.nextWaiter != null) {
unlinkCancelledWaiters();
}
if (interruptMode != 0) {
reportInterruptAfterWait(interruptMode);
}
// 计算剩余时间
final long remaining = deadline - System.nanoTime(); // avoid overflow
return remaining <= initialNanos ? remaining : Long.MIN_VALUE;
}
  • 非阻塞读取元素
    /**
* 1)如果队列为空,则返回 null
* 2)如果队列非空,则移除并返回队列头部元素
* created by ZXD at 2 Dec 2018 T 09:41:30
* @return
*/
@Override
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return count == 0 ? null : dequeue();
} finally {
lock.unlock();
}
} /**
* 1)如果队列非空,则移除并返回头部元素。
* 2)如果队列为空,则最多在指定的超时时间内等待队列有元素可用,
* 如果有则移除并返回队列头部;如果已经超时,则返回 null.
*/
@Override
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
// 计算以纳秒为单位的超时时间
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) {
if (nanos <= 0L) {
return null;
}
// 在非空条件上最多阻塞 nanos 纳秒
nanos = notEmpty.awaitNanos(nanos);
}
return dequeue();
} finally {
lock.unlock();
}
}

ArrayBlockingQueue 源码分析的更多相关文章

  1. java多线程系列(九)---ArrayBlockingQueue源码分析

    java多线程系列(九)---ArrayBlockingQueue源码分析 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 j ...

  2. 死磕 java集合之ArrayBlockingQueue源码分析

    问题 (1)ArrayBlockingQueue的实现方式? (2)ArrayBlockingQueue是否需要扩容? (3)ArrayBlockingQueue有什么缺点? 简介 ArrayBloc ...

  3. 并发编程(八)—— Java 并发队列 BlockingQueue 实现之 ArrayBlockingQueue 源码分析

    开篇先介绍下 BlockingQueue 这个接口的规则,后面再看其实现. 阻塞队列概要 阻塞队列与我们平常接触的普通队列(LinkedList或ArrayList等)的最大不同点,在于阻塞队列的阻塞 ...

  4. Java并发编程笔记之ArrayBlockingQueue源码分析

    JDK 中基于数组的阻塞队列 ArrayBlockingQueue 原理剖析,ArrayBlockingQueue 内部如何基于一把独占锁以及对应的两个条件变量实现出入队操作的线程安全? 首先我们先大 ...

  5. 多线程高并发编程(12) -- 阻塞算法实现ArrayBlockingQueue源码分析(1)

    一.前言 前文探究了非阻塞算法的实现ConcurrentLinkedQueue安全队列,也说明了阻塞算法实现的两种方式,使用一把锁(出队和入队同一把锁ArrayBlockingQueue)和两把锁(出 ...

  6. Java核心复习——J.U.C ArrayBlockingQueue源码分析

    介绍 依赖关系 源码 构造方法 public ArrayBlockingQueue(int capacity) { this(capacity, false);//默认构造非公平的有界队列 } pub ...

  7. ArrayBlockingQueue源码分析

    ArrayBlockingQueue是一个基于数组实现的有界的阻塞队列. 属性 //底层存储元素的数组.为final说明一旦初始化,容量不可变,所以是有界的. final Object[] items ...

  8. 【JUC】JDK1.8源码分析之ArrayBlockingQueue(三)

    一.前言 在完成Map下的并发集合后,现在来分析ArrayBlockingQueue,ArrayBlockingQueue可以用作一个阻塞型队列,支持多任务并发操作,有了之前看源码的积累,再看Arra ...

  9. JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue

    JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...

随机推荐

  1. 数塔 Medium

    Summer is coming! It's time for Iahub and Iahubina to work out, as they both want to look hot at the ...

  2. Building and booting Nexus 5 kernel

    1. Downloading toolchain and setup. git clone https://android.googlesource.com/platform/prebuilts/gc ...

  3. React事件绑定的几种方式对比

    React事件绑定 由于类的方法默认不会绑定this,因此在调用的时候如果忘记绑定,this的值将会是undefined.通常如果不是直接调用,应该为方法绑定this.绑定方式有以下几种: 1. 在构 ...

  4. 一张图说明移动前端开发与web前端开发的区别

  5. DEV控件的分页控件,实现勾选复选框

    /// <summary> /// 单元格的点击事件 /// </summary> /// <param name="sender"></ ...

  6. django基础篇02-url路由系统

    django的路由系统: 一.基本用法: 1.path('index', views.index), # 通过类的方式创建url映射 2.path('home', views.Home.as_view ...

  7. Java常用日期处理方法

    import org.apache.commons.lang3.time.FastDateFormat; import org.joda.time.DateTime; import org.apach ...

  8. MySQL数据类型和约束条件

    一.数据库操作数据的存储引擎 INNODB:支持事务 行锁 外键 查询速度比MYSiam慢 但是保证了数据的安全性 5.1 版本之后 MYSIAM:老版本用 5.1版本之前 搜索速度快 不支持事务 没 ...

  9. Comet OJ - Contest #2 C题 言论的阴影里妄想初萌

    题目描述 Takuru 是一名能力者,他在地震时获得了念力致动的能力.所以他经常用自己的能力去干一些奇奇怪怪的事情. 有一天他获得了一张 nn 个点的无向完全图,之后他使用了能力,导致这张图的 \fr ...

  10. GUI学习之n——自定义信号

    PyQt中是依靠信号——槽的机制来运行,可是有些时候需要我们定义一下些新的信号以满足使用要求,例如按钮是没有右击的效果,那我们怎么创建一个新的信号呢? 首先要注意,新的信号是要以类属性的形式来定义 c ...