ArrayBlockingQueue源码解析(1)
此文已由作者赵计刚授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
注意:在阅读本文之前或在阅读的过程中,需要用到ReentrantLock,内容见《第五章 ReentrantLock源码解析1--获得非公平锁与公平锁lock()》《第六章 ReentrantLock源码解析2--释放锁unlock()》《第七章 ReentrantLock总结》
1、对于ArrayBlockingQueue需要掌握以下几点
创建
入队(添加元素)
出队(删除元素)
2、创建
public ArrayBlockingQueue(int capacity, boolean fair)
public ArrayBlockingQueue(int capacity)
使用方法:
Queue<String> abq = new ArrayBlockingQueue<String>(2);
Queue<String> abq = new ArrayBlockingQueue<String>(2,true);
通过使用方法,可以看出ArrayBlockingQueue支持ReentrantLock的公平锁模式与非公平锁模式,对于这两种模式,查看本文开头的文章即可。
源代码如下:
private final E[] items;//底层数据结构
private int takeIndex;//用来为下一个take/poll/remove的索引(出队)
private int putIndex;//用来为下一个put/offer/add的索引(入队)
private int count;//队列中元素的个数 /*
* Concurrency control uses the classic two-condition algorithm found in any
* textbook.
*/ /** Main lock guarding all access */
private final ReentrantLock lock;//锁
/** Condition for waiting takes */
private final Condition notEmpty;//等待出队的条件
/** Condition for waiting puts */
private final Condition notFull;//等待入队的条件
/**
* 创造一个队列,指定队列容量,指定模式
* @param fair
* true:先来的线程先操作
* false:顺序随机
*/
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = (E[]) new Object[capacity];//初始化类变量数组items
lock = new ReentrantLock(fair);//初始化类变量锁lock
notEmpty = lock.newCondition();//初始化类变量notEmpty Condition
notFull = lock.newCondition();//初始化类变量notFull Condition
} /**
* 创造一个队列,指定队列容量,默认模式为非公平模式
* @param capacity <1会抛异常
*/
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
注意:
ArrayBlockingQueue的组成:一个对象数组+1把锁ReentrantLock+2个条件Condition
在查看源码的过程中,也要模仿带条件锁的使用,这个双条件锁模式是很经典的模式
3、入队
3.1、public boolean offer(E e)
原理:
在队尾插入一个元素, 如果队列没满,立即返回true; 如果队列满了,立即返回false
使用方法:
abq.offer("hello1");
源代码:
/**
* 在队尾插入一个元素,
* 如果队列没满,立即返回true;
* 如果队列满了,立即返回false
* 注意:该方法通常优于add(),因为add()失败直接抛异常
*/
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)//数组满了
return false;
else {//数组没满
insert(e);//插入一个元素
return true;
}
} finally {
lock.unlock();
}
}
private void insert(E x) {
items[putIndex] = x;//插入元素
putIndex = inc(putIndex);//putIndex+1
++count;//元素数量+1
/**
* 唤醒一个线程
* 如果有任意一个线程正在等待这个条件,那么选中其中的一个区唤醒。
* 在从等待状态被唤醒之前,被选中的线程必须重新获得锁
*/
notEmpty.signal();
}
/**
* i+1,数组下标+1
*/
final int inc(int i) {
return (++i == items.length) ? 0 : i;
}
代码非常简单,流程看注释即可,只有一点注意点:
在插入元素结束后,唤醒等待notEmpty条件(即获取元素)的线程,可以发现这类似于生产者-消费者模式
3.2、public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException
原理:
在队尾插入一个元素,,如果数组已满,则进入等待,直到出现以下三种情况:
被唤醒
等待时间超时
当前线程被中断
使用方法:
try {
abq.offer("hello2",1000,TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
源代码:
/**
* 在队尾插入一个元素,
* 如果数组已满,则进入等待,直到出现以下三种情况:
* 1、被唤醒
* 2、等待时间超时
* 3、当前线程被中断
*/
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException { if (e == null)
throw new NullPointerException();
long nanos = unit.toNanos(timeout);//将超时时间转换为纳秒
final ReentrantLock lock = this.lock;
/*
* lockInterruptibly():
* 1、 在当前线程没有被中断的情况下获取锁。
* 2、如果获取成功,方法结束。
* 3、如果锁无法获取,当前线程被阻塞,直到下面情况发生:
* 1)当前线程(被唤醒后)成功获取锁
* 2)当前线程被其他线程中断
*
* lock()
* 获取锁,如果锁无法获取,当前线程被阻塞,直到锁可以获取并获取成功为止。
*/
lock.lockInterruptibly();//加可中断的锁
try {
for (;;) {
if (count != items.length) {//队列未满
insert(e);
return true;
}
if (nanos <= 0)//已超时
return false;
try {
/*
* 进行等待:
* 在这个过程中可能发生三件事:
* 1、被唤醒-->继续当前这个for(;;)循环
* 2、超时-->继续当前这个for(;;)循环
* 3、被中断-->之后直接执行catch部分的代码
*/
nanos = notFull.awaitNanos(nanos);//进行等待(在此过程中,时间会流失,在此过程中,线程也可能被唤醒)
} catch (InterruptedException ie) {//在等待的过程中线程被中断
notFull.signal(); // 唤醒其他未被中断的线程
throw ie;
}
}
} finally {
lock.unlock();
}
}
注意:
awaitNanos(nanos)是AQS中的一个方法,这里就不详细说了,有兴趣的自己去查看AQS的源代码。
lockInterruptibly()与lock()的区别见注释
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 事务消息中心-TMC
【推荐】 3分钟带你了解负载均衡服务
【推荐】 Kudu vs HBase
ArrayBlockingQueue源码解析(1)的更多相关文章
- 第八章 ArrayBlockingQueue源码解析
注意:在阅读本文之前或在阅读的过程中,需要用到ReentrantLock,内容见<第五章 ReentrantLock源码解析1--获得非公平锁与公平锁lock()><第六章 Reen ...
- Java并发包源码学习系列:阻塞队列实现之ArrayBlockingQueue源码解析
目录 ArrayBlockingQueue概述 类图结构及重要字段 构造器 出队和入队操作 入队enqueue 出队dequeue 阻塞式操作 E take() 阻塞式获取 void put(E e) ...
- ArrayBlockingQueue源码解析(2)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3.3.public void put(E e) throws InterruptedException 原 ...
- Java源码解析——集合框架(二)——ArrayBlockingQueue
ArrayBlockingQueue源码解析 ArrayBlockingQueue是一个阻塞式的队列,继承自AbstractBlockingQueue,间接的实现了Queue接口和Collection ...
- Java并发包源码学习系列:阻塞队列实现之LinkedBlockingQueue源码解析
目录 LinkedBlockingQueue概述 类图结构及重要字段 构造器 出队和入队操作 入队enqueue 出队dequeue 阻塞式操作 E take() 阻塞式获取 void put(E e ...
- Java并发包源码学习系列:阻塞队列实现之PriorityBlockingQueue源码解析
目录 PriorityBlockingQueue概述 类图结构及重要字段 什么是二叉堆 堆的基本操作 向上调整void up(int u) 向下调整void down(int u) 构造器 扩容方法t ...
- Java并发包源码学习系列:阻塞队列实现之DelayQueue源码解析
目录 DelayQueue概述 类图及重要字段 Delayed接口 Delayed元素案例 构造器 put take first = null 有什么用 总结 参考阅读 系列传送门: Java并发包源 ...
- Java并发包源码学习系列:阻塞队列实现之SynchronousQueue源码解析
目录 SynchronousQueue概述 使用案例 类图结构 put与take方法 void put(E e) E take() Transfer 公平模式TransferQueue QNode t ...
- Java并发包源码学习系列:阻塞队列实现之LinkedTransferQueue源码解析
目录 LinkedTransferQueue概述 TransferQueue 类图结构及重要字段 Node节点 前置:xfer方法的定义 队列操作三大类 插入元素put.add.offer 获取元素t ...
随机推荐
- TFS SDK
vs2013 已包含. 可参考 TFS SDK: Connecting to TFS 2010 & TFS 2012 Programmatically http://geekswithblog ...
- rpm安装jdk7
原文:http://www.centoscn.com/image-text/config/2015/0208/4658.html 系统环境:centos-6.5 安装方式:rpm安装 软件:jdk-7 ...
- 2016-2017-2 20155312 实验二《Java面向对象程序设计》实验报告
知识总结 伪代码 产品代码 Java编程时,程序员对类实现的测试叫单元测试. 测试用例是为某个特殊目标而编制的一组测试输入.执行条件以及预期结果,以便测试某个程序路径或核实是否满足某个特定需求. 先写 ...
- python之数据库内置方法以及pymysql的使用
一.mysql内置方法 1)视图的概念和用法 .什么是视图 视图就是通过查询得到一张虚拟表,然后保存下来,下次用的直接使用即可 .为什么要用视图 如果要频繁使用一张虚拟表,可以不用重复查询 .如何用视 ...
- [ExecuteInEditMode]
ExecuteInEditMode属性的作用是在EditMode下也可以执行脚本.Unity中默认情况下,脚本只有在运行的时候才被执行,加上此属性后,不运行程序,也能执行脚本. 与PlayMode不同 ...
- rails 查看项目的所有路由
rails routes
- 2019.01.22 hdu5195 DZY Loves Topological Sorting(贪心+线段树)
传送门 题意简述:给出一张DAGDAGDAG,要求删去不超过kkk条边问最后拓扑序的最大字典序是多少. 思路:贪心帮当前不超过删边上限且权值最大的点删边,用线段树维护一下每个点的入度来支持查询即可. ...
- 使用bat批处理文件定时自动备份oracle数据库并上传ftp服务器
一.使用bat批处理文件备份oracle(前提是配置好oracle数据库客户端) @echo off set databasename=orcl //数据库名 set username=ninic ...
- SQL语句之奇形怪状的冷门函数
lag() over() ) OVER(ORDER BY C.column) FROM Table C; 第一条记录已经无法再取前一条记录,所以LAG()函数返回空. SQL为意思如下. LAG(C. ...
- UVaLive 3641 Leonardo's Notebook (置换)
题意:给定一个置换 B 问是否则存在一个置换 A ,使用 A^2 = B. 析:可以自己画一画,假设 A = (a1, a2, a3)(b1, b2, b3, b4),那么 A^2 = (a1, a2 ...