ArrayBlockingQueue 和LinkedBlockQueue
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的更多相关文章
- JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue
JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...
- 【JUC】JDK1.8源码分析之ArrayBlockingQueue(三)
一.前言 在完成Map下的并发集合后,现在来分析ArrayBlockingQueue,ArrayBlockingQueue可以用作一个阻塞型队列,支持多任务并发操作,有了之前看源码的积累,再看Arra ...
- 阅读ArrayBlockingQueue源码了解如何利用锁实现BlockingQueue
BlockingQueue是多线程里面一个非常重要的数据结构.在面试的时候,也常会被问到怎么实现BlockingQueue.本篇根据Java7里ArrayBlockingQueue的源码,简单介绍一下 ...
- JAVA可阻塞队列-ArrayBlockingQueue子类BlockingQueue的应用,使用它来实现子线程打印10次,主线程打印100次,如此反复
/** * 使用BlockingQueue实现主子线程互相打印 * @author duwenlei * */ public class BlockingQueueTest { public stat ...
- JAVA可阻塞队列-ArrayBlockingQueue
在前面的的文章,写了一个带有缓冲区的队列,是用JAVA的Lock下的Condition实现的,但是JAVA类中提供了这项功能,就是ArrayBlockingQueue, ArrayBlockingQu ...
- Java多线程系列--“JUC集合”07之 ArrayBlockingQueue
概要 本章对Java.util.concurrent包中的ArrayBlockingQueue类进行详细的介绍.内容包括:ArrayBlockingQueue介绍ArrayBlockingQueue原 ...
- Java并发之BlockingQueue 阻塞队列(ArrayBlockingQueue、LinkedBlockingQueue、DelayQueue、PriorityBlockingQueue、SynchronousQueue)
package com.thread.test.thread; import java.util.Random; import java.util.concurrent.*; /** * Create ...
- ArrayBlockingQueue跟LinkedBlockingQueue的区别
.队列中的锁的实现不同 ArrayBlockingQueue中的锁是没有分离的,即生产和消费用的是同一个锁: LinkedBlockingQueue中的锁是分离的,即生产用的是putLock,消费是t ...
- ArrayBlockingQueue,BlockingQueue分析
BlockingQueue接口定义了一种阻塞的FIFO queue,每一个BlockingQueue都有一个容量,让容量满时往BlockingQueue中添加数据时会造成阻塞,当容量为空时取元素操作会 ...
随机推荐
- detours编译与windows下makefile学习
1.编译 windows环境命令行编译很少用,detours需要使用命令行编译,刚好试试,过程如下: 1.为了能够在所有目录中使用nmake命令,需要设置环境变量Path D:\Program Fil ...
- LeetCode 817. Linked List Components (链表组件)
题目标签:Linked List 题目给了我们一组 linked list, 和一组 G, 让我们找到 G 在 linked list 里有多少组相连的部分. 把G 存入 hashset,遍历 lin ...
- 20140307 引用赋值、类的初始化、指针数组、数组指针、new
引用不能被赋值http://blog.csdn.net/laixingjun/article/details/9005200 类构造函数两种初始化方法区别,哪种好:http://blog.163.co ...
- JVM 与 Linux 的内存关系详解
来源:美团技术团队 在一些物理内存为8g的服务器上,主要运行一个Java服务,系统内存分配如下:Java服务的JVM堆大小设置为6g,一个监控进程占用大约 600m,Linux自身使用大约800m. ...
- EFCore学习记录笔记
1:连接slqlocaldb数据库 (1)在CMD下可以输入sqllocaldb info 查看本机安装的所有的localdb实例 (2)数据库连接字符串为:“Server=(localdb)\\MS ...
- nodejs入门安装与调试,mac环境
install nvm (node version manager) 安装nvm curl -o- https://raw.githubusercontent.com/creationix/nvm/v ...
- META标签的定义与使用(二、页面描述信息(NAME))
二.name的content指定实际内容.如:如果指定level(等级)为value(值),则Content可能是beginner(初级).intermediate(中级).advanced(高级). ...
- fuzzy commitment 和fuzzy vault
Alice,这位令人惊异的魔术天才,正表演关于人类意念的神秘技巧.她将在Bob选牌之前猜中Bob将选的牌!注意Alice在一张纸上写出她的预测.Alice很神秘地将那张纸片装入信封中并封上.就在人们吃 ...
- 从零开始:Mysql基于Amoeba的集群搭建
从零开始:Mysql基于Amoeba的集群搭建 准备环境 1.mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz 2.amoeba-mysql-binary-2.0. ...
- android studio toolbar遮挡住下面控件内容
只需要在该控件布局(content_***.xml)加入: app:layout_behavior="@string/appbar_scrolling_view_behavior" ...