不允许还有Java程序员不了解BlockingQueue阻塞队列的实现原理( 二 )


4.2 put方法源码解析

不允许还有Java程序员不了解BlockingQueue阻塞队列的实现原理

文章插图
无论是放数据还是取数据都是从队头开始,逐渐往队尾移动 。
// 放数据 , 如果队列已满 , 就一直阻塞,直到有其他线程从队列中取走数据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();}}// 实际往队列添加数据的方法private void enqueue(E x) {// 获取数组final Object[] items = this.items;// putIndex 表示本次插入的位置items[putIndex] = x;// ++putIndex 计算下次插入的位置// 如果本次插入的位置,正好是队尾,下次插入就从 0 开始if (++putIndex == items.length)putIndex = 0;// 元素数量加一count++;// 唤醒因为队列空等待的线程notEmpty.signal();}源码中有个有意思的设计,添加元素的时候如果已经到了队尾,下次就从队头开始添加,相当于做成了一个循环队列 。
像下面这样:
不允许还有Java程序员不了解BlockingQueue阻塞队列的实现原理

文章插图
4.3 take方法源码// 取数据 , 如果队列为空,就一直阻塞 , 直到有其他线程往队列中放数据public E take() throws InterruptedException {final ReentrantLock lock = this.lock;// 加锁,加可中断的锁lock.lockInterruptibly();try {// 如果队列为空 , 就一直阻塞,直到被唤醒while (count == 0)notEmpty.await();// 如果队列不为空,就从队列取数据return dequeue();} finally {// 结束后,别忘了释放锁lock.unlock();}}// 实际从队列取数据的方法private E dequeue() {// 获取数组final Object[] items = this.items;// takeIndex 表示本次取数据的位置 , 是上一次取数据时计算好的E x = (E) items[takeIndex];// 取完之后,就把队列该位置的元素删除items[takeIndex] = null;// ++takeIndex 计算下次取数据的位置// 如果本次取数据的位置,正好是队尾,下次就从 0 开始取数据if (++takeIndex == items.length)takeIndex = 0;// 元素数量减一count--;if (itrs != null)itrs.elementDequeued();// 唤醒被队列满所阻塞的线程notFull.signal();return x;}4.4 总结
  1. ArrayBlockingQueue基于数组实现的阻塞队列,创建队列时需指定容量大?。?是有界队列 。
  2. ArrayBlockingQueue底层采用循环队列的形式,保证数组位置可以重复使用 。
  3. ArrayBlockingQueue存取都采用ReentrantLock加锁,保证线程安全,在多线程环境下也可以放心使用 。
  4. 使用ArrayBlockingQueue的时候,预估好队列长度,保证生产者和消费者速率相匹配 。
我是「一灯架构」,如果本文对你有帮助 , 欢迎各位小伙伴点赞、评论和关注,感谢各位老铁,我们下期见

不允许还有Java程序员不了解BlockingQueue阻塞队列的实现原理

文章插图

推荐阅读