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. 2019 牛客多校第二场 H Second Large Rectangle

    题目链接:https://ac.nowcoder.com/acm/contest/882/H 题目大意 给定一个 n * m 的 01 矩阵,求其中第二大的子矩阵,子矩阵元素必须全部为 1.输出其大小 ...

  2. 创建第一个spirngmvc小项目

    题外: 设置目录为源代码目录 1.进入:file->project structure->modules->soures 进入这个里面,选择相应的文件夹.例如src/java里的ja ...

  3. EFCore学习记录笔记

    1:连接slqlocaldb数据库 (1)在CMD下可以输入sqllocaldb info 查看本机安装的所有的localdb实例 (2)数据库连接字符串为:“Server=(localdb)\\MS ...

  4. OpenCV3 VideoCapture buffer

    在ubuntu16.04下写关于opencv的工程,在调用摄像头时发现VideoCapture有5帧的buffer,所以采用5个capture>>mat来处理. if (FlagConti ...

  5. Android开发 了解ViewModel

    前言 ViewModel是google推出的一个数据处理框架,ViewModel类是被设计用来以可感知生命周期的方式存储和管理 UI 相关数据ViewModel中数据会一直存活即使 activity ...

  6. Unable to resolve dependency for ':app@debug/compileClasspath': Could not resolve com.android.support:appcompat-v7:26.1.0

    android studio  3.0 出现此问题可能是因为 你的android studio 时脱机状态 无法下载资源 这时候你可以点击左上角分File->Other Settings-> ...

  7. Cookie 干货

    从前端开发看Cookie Cookie是浏览器端的存储机制 存在意义: 为了解决“如何记住用户信息”而发明的: 当用户访问网页时,他的名字可以存储在cookie中 下次用户访问该页面时,cookie会 ...

  8. window 系统上传文件到linux 系统出现dos 格式换行符

    Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行,所以为了避免这种情况的发生,我们可以 ...

  9. 如何设置树莓派 VNC 的分辨率

    当我们使用 VNC 连接到树莓派时,默认的分辨率非常低.甚至无法显示整个桌面,因此我们需要对分辨率进行设置.在树莓派上设置 VNC 的分辨率很简单,在终端运行下面指令进入设置界面设置. 1 sudo ...

  10. Remember, every day is a miracle.

    Remember, every day is a miracle.请记住,每一天都是一个奇迹.