ArrayBlockingQueue

ArrayBlockingQueue是Java多线程常用的线程安全的一个集合,基于数组实现,继承自AbstractQueue,实现了BlockingQueue和Serializable接口。
//先看看器内部的成员变量:

private static final long serialVersionUID = -817911632652898426L;//实现了序列化接口

/** 基于数组的实现,内部持有一个Object数组 */
final Object[] items; /** 数据读取指针 */
int takeIndex; /** 数据插入指针 */
int putIndex; /** 当前队列中元素的总数 */
int count; /** 采用了ReentrantLock 的实现 */
final ReentrantLock lock; /** 标识当前队列中有可读元素 */
private final Condition notEmpty; /** 标识当前队列可写入 */
private final Condition notFull; //可以看到,ArrayBlockingQueue内部维护了一个takeIndex指针和一个putIndex指针,分别用于读取和写入;一个notEmpty和一个notFull,分别用于保证写入和读取的线程安全,唤醒读取和写入线程
//再看看构造函数
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];//初始化数组
lock = new ReentrantLock(fair);//初始化ReentrantLock,并标识是否为公平锁
notEmpty = lock.newCondition();
notFull = lock.newCondition();
} //然后来看看ArrayBlockingQueue的offer方法 public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
//如果队列满,则添加失败。offer方法不会阻塞,put方法会阻塞
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
//首先做空值检查,如果为空,抛出空值异常。然后使用了ReentrantLock ,来保证offer的线程安全性。下面来看看真正的添加方法enqueue:
private void enqueue(E x) {
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
}
//可以看到,ArrayBlockingQueue内部维护了一个putIndex 指针,该指针指向当前队列可以插入的位置,直接将当前的Object对象插入到inputIndex位置,然后让inputIndex自增,如果队列已满,则指向第一个元素。最后元素总数加一,并唤醒读线程
//最后我们来看读取take方法:
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();//take方法是阻塞的,poll方法不会阻塞,直接返回。
return dequeue();
} finally {
lock.unlock();
}
}
//可以看到,那么take方法将被阻塞。下面看看出对方法dequeue:
private E dequeue() {
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;//如果取到最后一个元素,takeIndex 指向第一个元素
count--;//元素总数减一
if (itrs != null)
itrs.elementDequeued();
notFull.signal();//唤醒写入线程
return x;
}
以上便是ArrayBlockingQueue的基本方法,内部锁的实现是ReentrantLock ,维护了take和put两个指针;入队和出对方法也都挺简单的,需要注意的是,take和put方法是阻塞的,offer、add、poll等方法是非阻塞的

LinkedBlockingQueue

LinkedBlockingQueue基于链表实现,继承了AbstractQueue,实现了序列化接口Serializable和BlockingQueue接口
 //首先看看内部成员变量:

private final int capacity;

/** count用来记录内部元素的总数 */
private final AtomicInteger count = new AtomicInteger(); /** Node节点的头指针*/
transient Node<E> head; /** 尾指针*/
private transient Node<E> last; /** 读锁 */
private final ReentrantLock takeLock = new ReentrantLock(); /** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition(); /** 写锁 */
private final ReentrantLock putLock = new ReentrantLock(); /** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();
可以看到,与ArrayBlockQueue不同,元素总数使用了原子类AtomicInteger ,内部多维护了两把锁,读锁和写锁。其实现相对更加复杂
//下面看看其构造方法
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();//容量不能小于0
this.capacity = capacity;
last = head = new Node<E>(null);//初始化头尾指针
}
//下面是offer方法
public boolean offer(E e) {
if (e == null) throw new NullPointerException();//不接受空值
final AtomicInteger count = this.count;
if (count.get() == capacity)//如果当前元素总数等于其容量大小,直接返回false
return false;
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (count.get() < capacity) {
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return c >= 0;
}
//我们可以看到,LingkedBlockQueue是不接受空值的。offer是非阻塞的。入队之后,如果队列没有满,唤醒其他入队线程,并且唤醒出队线程。
//继续看入队方法enqueue
private void enqueue(Node<E> node) {
last = last.next = node;
}//可以看到入队方法相当简单,就是把尾节点的下一个节点直接指向新加入的节点,然后将新加入的节点作为尾节点 //然后看看take方法:
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();//take方法是阻塞的
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}//也挺简单的,就是先判断是否可以出队,不能则等待,否则出队,然后唤醒其他出队线程,并唤醒入队线程
//最后是出队方法:
private E dequeue() {
Node<E> h = head;
Node<E> first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
} // 直接将一个元素取出,然后首位元素置空
总结,从实现来看,相比ArrayBlockQueue,LinkedBlockQueue的加锁方法相对更加复杂,但是其入队和出队方法更加简单;和ArrayBlockQueue一样,take、put方法阻塞,offer、add、poll方法不会阻塞

ArrayBlockingQueue 和LinkedBlockQueue的更多相关文章

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

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

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

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

  3. 阅读ArrayBlockingQueue源码了解如何利用锁实现BlockingQueue

    BlockingQueue是多线程里面一个非常重要的数据结构.在面试的时候,也常会被问到怎么实现BlockingQueue.本篇根据Java7里ArrayBlockingQueue的源码,简单介绍一下 ...

  4. JAVA可阻塞队列-ArrayBlockingQueue子类BlockingQueue的应用,使用它来实现子线程打印10次,主线程打印100次,如此反复

    /** * 使用BlockingQueue实现主子线程互相打印 * @author duwenlei * */ public class BlockingQueueTest { public stat ...

  5. JAVA可阻塞队列-ArrayBlockingQueue

    在前面的的文章,写了一个带有缓冲区的队列,是用JAVA的Lock下的Condition实现的,但是JAVA类中提供了这项功能,就是ArrayBlockingQueue, ArrayBlockingQu ...

  6. Java多线程系列--“JUC集合”07之 ArrayBlockingQueue

    概要 本章对Java.util.concurrent包中的ArrayBlockingQueue类进行详细的介绍.内容包括:ArrayBlockingQueue介绍ArrayBlockingQueue原 ...

  7. Java并发之BlockingQueue 阻塞队列(ArrayBlockingQueue、LinkedBlockingQueue、DelayQueue、PriorityBlockingQueue、SynchronousQueue)

    package com.thread.test.thread; import java.util.Random; import java.util.concurrent.*; /** * Create ...

  8. ArrayBlockingQueue跟LinkedBlockingQueue的区别

    .队列中的锁的实现不同 ArrayBlockingQueue中的锁是没有分离的,即生产和消费用的是同一个锁: LinkedBlockingQueue中的锁是分离的,即生产用的是putLock,消费是t ...

  9. ArrayBlockingQueue,BlockingQueue分析

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

随机推荐

  1. gnome/KDE安装,gnome出现问题,重新安装nvdia驱动, Linux(CentOS7) NVIDIA GeForece GTX 745 显卡驱动

    新安装显示gtx745驱动NVIDIA-Linux-x86_64-346.59.run, yum groupremove kde-desktop yum groupinstall "Desk ...

  2. 2019 牛客多校第一场 C Euclidean Distance ?

    题目链接:https://ac.nowcoder.com/acm/contest/881/C 题目大意 给定 m 和 n 个整数 ai,$-m \leq a_i \leq m$,求$\sum\limi ...

  3. 20140502 static_cast和dynamic_cast的类型检查 双链表建立,删除,打印

    1.static_cast和dynamic_cast的类型检查 static_cast的类型检查:只检查无关类之间的转换 CBaseY* pY1 = static_cast<CBaseY*> ...

  4. angularJS和requireJS和angularAMD

    最近因为要用到angularJS开发项目,因为涉及到的静态资源比较多,所以想把js文件通过requireJS来按需加载,这两个框架以前都使用过,但是结合到一起还没有用过,那就试一下,看能否达到目的. ...

  5. SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT'OpenRowset/OpenDatasource' 的访问 (也就是跨数据库访问出错)

    delphi ado 跨数据库访问 语句如下 ' and db = '帐套1' 报错内容是:SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATE ...

  6. centos6.5离线安装pandas

    由于笔记本跑不动很多大赛的数据,遂转至申请的虚拟机(centos6.5)中,奈何学校的虚拟机没网,就开始了离线安装pandas的道路,当中有踩了好些坑,于是便产生了这篇文章~ 首先,自带的python ...

  7. vue 挂载点 实例 模板

    挂载点:vue实例 里面的el属性 对应的 id 所在的dom节点 模板:指的是挂载点内部的内容           模板可以写在挂载点内部           也可以写在属性里面 demo < ...

  8. Android App上架流程

    想要把APP上架到应用市场都要先注册开发者账号才可以.这里的方法包括注册帐号和后期上架及一些需要注意的问题.注意:首次提交应用绝对不能随便删除,否则后面再提交会显示应用APP冲突,会要求走应用认领流程 ...

  9. div+css对网页进行布局

    div+css对网页进行布局 首先在页面整体上进行div标签划分内容区域,然后再用css进行定位,最后再对相应的区域添加内容. 1.用div将页面划分 拿到网站页面图后,首先将分析页面分为哪几块,然后 ...

  10. windows安装cygwin实现gcc/g++/linux操作等功能

    首先安装cygwin:参照下列博客去cygwin官网下载即可.安装过程中记得勾选需要的安装包,比如gcc/gdb https://blog.csdn.net/qilvmilv/article/deta ...