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. 4.RabbitMQ Linux安装

    这里使用的Linux是CentOS6.2 将/etc/yum.repo.d/目录下的所有repo文件删除 先下载epel源 # wget -O  /etc/yum.repos.d/epel-erlan ...

  2. servlet和servlet-mapping的作用

    转载:https://www.jianshu.com/p/6dadc489969a 某个工程的 web.xml 文件片段:   执行顺序 访问顺序为1—>2—>3—>4,其中2和3的 ...

  3. Tomacat7启动报错-org.apache.catalina.deploy.WebXml addFilter

    java.lang.NoSuchMethodException: org.apache.catalina.deploy.WebXml addFilter at org.apache.tomcat.ut ...

  4. Xpath-Assertion断言

  5. 论文阅读笔记---ShuffleNet V1

    01 ShuffleNet V1要解决什么问题 为算力有限的嵌入式场景下专门设计一个高效的神经网络架构. 02 亮点 使用了两个新的操作:pointwise group convolution和cha ...

  6. 5个CSS3技术实现设计增强

    层叠样式表(css)是Web设计的一种语言,CSS的下一代版本CSS3已经蓄势待发.你是否可望开始使用它们却又不知从何下手呢?虽然还有一些新属性没有得到官方的确认,但是一些浏览器已经开始支持来自CSS ...

  7. java笔试之查找组成一个偶数最接近的两个素数

    任意一个偶数(大于2)都可以由2个素数组成,组成偶数的2个素数有很多种情况,本题目要求输出组成指定偶数的两个素数差值最小的素数对. package test; import java.util.Sca ...

  8. 十个非常实用的MySQL命令

      建赟  版主 楼主   前言 今天介绍一些MySQL常用的实用命令,都是一些比较简单的命令.已经知道的朋友,就当是巩固吧,不知道的童鞋,可以好好在自己的机器上,练习下. 0. 显示数据库 命令:s ...

  9. 基于第三方开源库的OPC服务器开发指南(4)——后记:与另一个开源库opc workshop库相关的问题

    平心而论,我们从样例服务器的代码可以看出,利用LightOPC库开发OPC服务器还是比较啰嗦的,网上有人提出opc workshop库就简单很多,我千辛万苦终于找到一个05年版本的workshop库源 ...

  10. mysql 数据库基本命令

    停止mysql服务:net stop mysql      //管理员方式运行 启动mysql服务:net start mysql 进入数据库:mysql -u root -p 查看数据库:show ...