对于BlockingQueue的具体实现,主要关注的有两点:线程安全的实现和阻塞操作的实现。所以分析ArrayBlockingQueue也是基于这两点。

对于线程安全来说,所有的添加元素的方法和拿走元素的方法都会涉及到,我们通过分析offer方法和poll()方法就能看出线程安全是如何实现的。

首先来看offer方法

    public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
insert(e);
return true;
}
} finally {
lock.unlock();
}
}

通过代码可以看出是通过采用Lock的方式来获取锁,然后再进行插入操作,最后再释放锁。

因此对于poll方法来说实现的方法肯定也是大同小异

    public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : extract();
} finally {
lock.unlock();
}
}

说过了线程安全的实现,接下来说说阻塞是如何实现的。如果各位知道Object的wait/notify的话就很好理解了。这里涉及到一个接口叫java.util.concurrent.locks.Condition。

Condition拥有类似的操作:await/signal。Condition和一个Lock相关,由Lock的newCondition来创建。只有当前线程获取了这把锁,才能调用Condition的await方法来等待通知,否则会抛出异常。

下面来看看put方法就会明白如何使用一个Condition了

notFull =  lock.newCondition();
    public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
insert(e);
} finally {
lock.unlock();
}
}

实现阻塞的关键就是就是这个notFull的Condition,当队列已满,await方法会阻塞当前线程,并且释放Lock,等待其他线程调用notFull的signal来唤醒这个阻塞的线程。那么这个操作必然会在拿走元素的操作中出现,这样一旦有元素被拿走,阻塞的线程就会被唤醒。

这里有个问题,发出signal的线程肯定拥有这把锁的,因此await方法所在的线程肯定是拿不到这把锁的,await方法不能立刻返回,需要尝试获取锁直到拥有了锁才可以从await方法中返回。

这就是阻塞的实现原理,也是所谓的线程同步。

同样对于take方法会有一个notEmpty的Condition。

    public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return extract();
} finally {
lock.unlock();
}
}

需要注意的是这里返回队列长度的时候也是需要锁的

    public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return extract();
} finally {
lock.unlock();
}
}

ArrayBlockingQueue的实现相对简单,只需要一把锁就可以搞定,下一篇关于LinkedBlockingQueue则会复杂不少,需要用到两把锁。

最新文章

  1. SQL 查找重复项及批量修改数据成固定格式
  2. SQL Server 2008 R2——根据数据查找表名和字段名 根据脏数据定位表和字段
  3. EXTJS4自学手册——EXT基本方法、属性(mixins多继承、statics、require)
  4. 开启梦幻般的webrtc之旅
  5. paypal之nodejs 框架 Kraken-js 源码分析
  6. [Effective JavaScript 笔记] 第13条:使用立即调用的函数表达式创建局部作用域
  7. bnuoj 20832 Calculating Yuan Fen(暴力模拟)
  8. 尝试解决IIS问题一些方法
  9. vue原来可以这样上手
  10. Mac osx 系统安装 eclipse
  11. RabbitMQ 在 web 页面 创建 exchange, queue, routing key
  12. 后端list集合中的数据传递到前台HTML中显示(表格形式)
  13. yarn如何全局安装命令以及和环境变量的关系
  14. Redis集群中的节点如何保证数据一致
  15. 并发编程---线程---开启方式---进程线程的区别----Thread的其他属性
  16. 重签名提示:无法对 jar 进行签名: java.util.zip.ZipException
  17. 25. IO流.md
  18. Data01-数据结构和算法绪论
  19. JAVA I/O(二)文件NIO
  20. SSM框架整合(实现从数据库到页面展示)

热门文章

  1. Linux下PHP连接MS SQLServer的办法
  2. 【NOIP2016 Day1 T2】天天爱跑步
  3. Log4j各级别日志重复打印的问题
  4. sql里的null和空的区别
  5. .Net Core程序的部署(FDD与SCD)
  6. LeetCode 33. Search in Rotated Sorted Array(在旋转有序序列中搜索)
  7. struts2系列(四):struts2国际化的多种方式
  8. angularJs 个人初探笔记
  9. CentOs7相对于CentOs6的常用命令变化
  10. body.clientHeight与documentElement.clientHeight