Java的阻塞队列,在实现时,使用到了lock和condition,下面是对其主要方法的介绍。
首先看一下,阻塞队列中使用到的锁。
/** Main lock guarding all access **/ final ReentrantLock lock; /** Condition for waiting takes **/ private final Condition notEmpty; /** Condition for waiting puts **/ private final Condition notFull;
主要的锁是一个可重入锁,根据注释,它是用来保证所有访问的同步。此外,还有2个condition,notEmpty用于take等待,notFull用于put等待。
两个condition的初始化方法如下:
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(); }
下面介绍一下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(); } }
进行put时,首先对待插入的元素进行了非null判断。然后获取锁。之后用一个循环进行判断,如果元素已满,那么,就调用notFull的await方法,进行阻塞。当有别的线程(其实是take元素的线程)调用notFull的siginal方法后,put线程会被唤醒。唤醒后再确认一下count是否小于items.length,如果是,则进行加入队列的操作。
下面介绍一下take方法,代码如下:
public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == 0) notEmpty.await(); return dequeue(); } finally { lock.unlock(); } }
进行take时,同样先要获取锁,然后判断元素个数是否为0,为0时需要等待在notEmpty条件上,等待被唤醒。唤醒之后,会再进行一次元素个数判断,然后进行出队列操作。
分析代码到这里的时候,我产生了一个疑问,如果当前队列慢了,执行put的线程在获取到锁之后,等待notFull条件上。那么,当执行take操作的线程想获取锁时,阻塞队列的锁已经被前面put的线程获取了,那么take将永远得不到机会执行。怎么回事呢?
后来,我查了condition的await方法,它的注释如下:
原因在await方法的作用上。因为condition是通过lock创建的,而调用condition的await方法时,会自动释放和condition关联的锁。所以说,当put线程被阻塞后,它实际已经释放了锁了。所以,当有take线程想执行时,它是可以获取到锁的。
另一个问题:当等待在condition上的线程被唤醒时,因为之前调用await前,已经获取了锁,那么被唤醒时,它是自动就拥有了锁,还是需要重新获取呢?
在await方法的注释中,有如下的一段话:
说明当等待在condition上的线程被唤醒时,它需要重新获取condition关联的锁,获取到之后,await方法才会返回。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对小牛知识库的支持。如果你想了解更多相关内容请查看下面相关链接
本文向大家介绍Java源码解析阻塞队列ArrayBlockingQueue功能简介,包括了Java源码解析阻塞队列ArrayBlockingQueue功能简介的使用技巧和注意事项,需要的朋友参考一下 本文基于jdk1.8进行分析。 阻塞队列是java开发时常用的一个数据结构。首先看一下阻塞队列的作用是什么。阻塞队列的作用,从源码中类的注释中来了解,是最清晰准确的。 ArrayBlockingQue
主要内容:1 LinkedBlockingDeque的概述,2 LinkedBlockingDeque的原理,2.1 主要属性,2.2 构造器,2.3 入队操作,2.4 出队操作,2.5 检查操作,2.6 size操作,2.7 迭代操作,3 LinkedBlockingDeque的总结基于JDK1.8详细介绍了LinkedBlockingDeque的底层源码实现,包括双端队列的入队列、出队列、迭代等操作源码。实际上LinkedBlockingDeque的源码还是非常简单的! 1 LinkedBl
主要内容:1 LinkedTransferQueue的概述,2 LinkedTransferQueue的原理,2.1 主要属性,2.2 构造器,2.3 xfer核心方法,2.4 入队操作,2.5 出队操作,2.6 传递操作,2.7 检查操作,2.8 size操作,2.9 迭代操作,2.10 执行流程,3 LinkedTransferQueue的总结基于JDK1.8详细介绍了LinkedTransferQueue的底层源码实现,包括入队、出队、传递等操作源码,以及相比于LinkedBlocking
主要内容:1 DelayQueue的概述,2 DelayQueue的原理,2.1 主要属性,2.2 构造器,2.3 入队操作,2.4 出队操作,2.5 检查操作,2.6 size操作,2.7 迭代操作,3 DelayQueue的应用,3.1 案例,4 DelayQueue的总结基于JDK1.8详细介绍了DelayQueue的底层源码实现,包括延迟出队的原理,以及入队列、出队列等操作的源码。 1 DelayQueue的概述 public class DelayQueue< E extends De
主要内容:1 PriorityBlockingQueue的概述,2 PriorityBlockingQueue的原理,2.1 主要属性,2.2 构造器,2.3 入队操作,2.4 出队操作,2.5 检查操作,2.6 size操作,2.7 迭代操作,3 PriorityBlockingQueue的案例,3.1 基本使用,3.2 优先任务队列,4 PriorityBlockingQueue的总结基于JDK1.8详细介绍了PriorityBlockingQueue的底层源码实现,包括小顶堆和优先级排序的
主要内容:1 ConcurrentLinkedQueue的概述,2 ConcurrentLinkedQueue的实现,2.1 基本结构,2.2 构造器,2.3 入队操作,2.4 出队操作,2.5 过程详解,2.6 获取操作,2.7 其他操作,3 ConcurrentLinkedQueue的总结基于JDK1.8详细介绍了ConcurrentLinkedQueue的底层源码实现,包括同步原理、入队操作、出队操作、获取操作等。 1 ConcurrentLinkedQueue的概述 public cla