java多线程系列(九)---ArrayBlockingQueue源码分析

目录

成员变量

  • 数组大小
    final Object[] items;
  • 下一个进队元素的下标
    /** items index for next take, poll, peek or remove */
int takeIndex;
  • 下一个出队元素的下标
    /** items index for next put, offer, or add */
int putIndex;
  • 队列中元素的数目
    /** Number of elements in the queue */
int count;
  • 出队和入队需要的锁
    /*
* Concurrency control uses the classic two-condition algorithm
* found in any textbook.
*/ /** Main lock guarding all access */
final ReentrantLock lock;
  • 出队条件
    /** Condition for waiting takes */
private final Condition notEmpty;
  • 入队条件

/** Condition for waiting puts */
private final Condition notFull;

构造方法

  • 配置容量,先创建是否公平公平访问
    public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
  • 从源码可以看到,创建一个object数组,然后创建一个公平或非公平锁,然后创建出队条件和入队条件

offer方法

public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
  • 首先检查是否为null
  • 然后lock锁住
  • 如果当前数目count已经为初始的时候容量,这时候自己返回false
  • 否则的话执行enqueue方法
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
}
  • 将新的元素添加到数组的下一个进队的位置
  • 然后notEmpty出队条件唤醒,这个时候可以进行出队
  • 执行enqueue后然后释放锁

指定时间的offer方法

 public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException { checkNotNull(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(e);
return true;
} finally {
lock.unlock();
}
}
  • 方法大致和前面的一致,不同的时候是当队列满的时候,会等待一段时间,此时入队条件等待一段时间,一段时间后继续进入循环进行判断队列还满
  • 当队列不满的时候执行enqueue

add方法

  • 调用父类的add方法
public boolean add(E e) {
return super.add(e);
}
  • 父类AbstractQueue的add方法
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
  • 执行offer方法,这个时候可以对比上面直接调用offer,offer方法如果入队失败会直接返回false,而add方法会抛出异常

put方法

public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
  • 和限定时间的offer方法不同,当队列满的时候,会一直等待,直到有人唤醒

poll方法

public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
  • 首先执行lock方法锁定
  • 如果当前队中无元素,那么返回null,否则执行dequeue方法
 private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();
return x;
}
  • 根据出队下标取出元素,然后将该位置置为null
  • 将出队下标加一,如果出队下标等于了数组的大小,出队下标置为0
  • 队中元素数量减一
  • notFull唤醒,此时可以唤醒入队阻塞的线程

指定时间的poll方法

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
return dequeue();
} finally {
lock.unlock();
}
}
  • 与offer的指定时间和没有指定时间类似,poll指定时间的方法和没有指定时间的poll思路大致是一样的
  • 当此时队列为空的,为等待一段时间,然后自动唤醒,继续进入循环,直到队列中有元素,然后执行dequeue方法

take方法

   public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
  • 和前面指定时间的poll方法不同,当队中为空的时候,会一直等待,直到被唤醒

总结

  • 入队
方法 特点
offer(E e) 队列满的时候,返回false
offer(E e, long timeout, TimeUnit unit) 队列满的时候,等待一段时间,释放锁,一段时间后,进入就绪状态
add(E e) 队列满的时候,直接抛出异常
put(E e) 队列慢的时候,线程阻塞,直到被唤醒
  • 出队
方法 特点
poll() 队列为空的时候,直接返回null
poll(long timeout, TimeUnit unit) 队列为空的时候,等待一段时间,释放锁,一段时候后,进入就绪状态
take() 队列为空的时候,一直等待,释放锁,直到被唤醒
  • 总体的设计思路,通过一个数组来模拟一个数组,出队和入队都是同步的,也就是同一时间只能有一个入队或者出队操作,然后在入队的时候,如果队列已满的话,根据方法的不同有不同的策略,可以直接返回或者抛出异常,也可以阻塞一段时间,等会在尝试入队,或者直接阻塞,直到有人唤醒。而出队的时候,如果为空可以直接返回,也可以等待一段时间然后再次尝试,也可以阻塞,直到有人唤醒

我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引发自己内心的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)

作者:jiajun 出处: http://www.cnblogs.com/-new/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。

java多线程系列(九)---ArrayBlockingQueue源码分析的更多相关文章

  1. java多线程系列:ThreadPoolExecutor源码分析

    前言 这篇主要讲述ThreadPoolExecutor的源码分析,贯穿类的创建.任务的添加到线程池的关闭整个流程,让你知其然所以然.希望你可以通过本篇博文知道ThreadPoolExecutor是怎么 ...

  2. Java并发系列[5]----ReentrantLock源码分析

    在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可 ...

  3. Java集合系列[4]----LinkedHashMap源码分析

    这篇文章我们开始分析LinkedHashMap的源码,LinkedHashMap继承了HashMap,也就是说LinkedHashMap是在HashMap的基础上扩展而来的,因此在看LinkedHas ...

  4. Java并发系列[2]----AbstractQueuedSynchronizer源码分析之独占模式

    在上一篇<Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析>中我们介绍了AbstractQueuedSynchronizer基本的一些概 ...

  5. Java并发系列[3]----AbstractQueuedSynchronizer源码分析之共享模式

    通过上一篇的分析,我们知道了独占模式获取锁有三种方式,分别是不响应线程中断获取,响应线程中断获取,设置超时时间获取.在共享模式下获取锁的方式也是这三种,而且基本上都是大同小异,我们搞清楚了一种就能很快 ...

  6. java集合系列之LinkedList源码分析

    java集合系列之LinkedList源码分析 LinkedList数据结构简介 LinkedList底层是通过双端双向链表实现的,其基本数据结构如下,每一个节点类为Node对象,每个Node节点包含 ...

  7. java集合系列之ArrayList源码分析

    java集合系列之ArrayList源码分析(基于jdk1.8) ArrayList简介 ArrayList时List接口的一个非常重要的实现子类,它的底层是通过动态数组实现的,因此它具备查询速度快, ...

  8. Java集合系列:-----------03ArrayList源码分析

    上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解ArrayLi ...

  9. java基础系列之ConcurrentHashMap源码分析(基于jdk1.8)

    1.前提 在阅读这篇博客之前,希望你对HashMap已经是有所理解的,否则可以参考这篇博客: jdk1.8源码分析-hashMap:另外你对java的cas操作也是有一定了解的,因为在这个类中大量使用 ...

随机推荐

  1. win10 UWP Hmac

    HMAC是密钥相关的哈希运算消息认证码,输入密钥和信息. 在uwp,Hmac在很多网络使用,我最近写qiniu SDK,把原来C#改为UWP,需要使用HMAC. 上传文件 <form metho ...

  2. 张高兴的 Windows 10 IoT 开发笔记:使用 MAX7219 驱动 8×8 点阵

    GitHub:https://github.com/ZhangGaoxing/windows-iot-demo/tree/master/MAX7219

  3. hashlib模块--摘要算法

    算法介绍: Python的hashlib提供了常见的摘要算法:MD5,SHA()等. 摘要算法,又称哈希算法,散列算法.通过一个函数,吧任意长度的字符串转换为固定长度的字符串(16进制) 摘要算法就是 ...

  4. UVa1630,Folding

    区间dp,记忆化搜就可以 st为原串 dp[p][q]存st[p]~st[q]的最优长度,f[p][q]存对应的最优串 从(0,len-1)开始搜,f[0][len-1]为所求ans,回溯条件为p== ...

  5. Windows 10下Docker使用经验谈

    最近一直在开发Apworks框架的案例代码,同时也在一起修复Apworks框架中的Bug和一些设计上的不足.遇到的一个普遍问题是,代码的调试过程需要依赖很多外部系统,比如MongoDB.Postgre ...

  6. [译]ASP.NET Core 2.0 会话状态

    问题 如何在ASP.NET Core 2.0中存储会话状态? 答案 创建一个空项目,修改Startup类的ConfigureServices()方法,添加会话状态服务和它后台的存储服务: public ...

  7. svn 提交 working copy is not up-to-date

    svn在提交时报错信息如下: working copy is not up-to-date svn:commit failed(details follow): svn:file "xxxx ...

  8. jerasure 2.0译文

    原文地址: 本文译者水平有限,如发现问题请批评指正 Jerasure 2.0:为方便存储相关应用开发的一个基于C开发的纠删码库 版本2.0 James S.Plank Kevin M.Greenan ...

  9. python 生成器和迭代器

    迭代器协议 1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个Stoplteration异常,以终止迭代(只能往后走不能往前退) 2.可迭代对象:实现了 ...

  10. CentOs 系统启动流程相关

    CentOS的启动流程 1)加载BIOS 的硬件信息,获取第一个启动设备 2)读取第一个启动设备MBR 的引导加载程序(grub) 的启动信息 3)加载核心操作系统的核心信息,核心开始解压缩,并尝试驱 ...