4.2 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();}}// 实际往队列添加数据的方法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();}
源码中有个有意思的设计,添加元素的时候如果已经到了队尾,下次就从队头开始添加,相当于做成了一个循环队列 。像下面这样:

文章插图
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 总结- ArrayBlockingQueue基于数组实现的阻塞队列,创建队列时需指定容量大?。?是有界队列 。
- ArrayBlockingQueue底层采用循环队列的形式,保证数组位置可以重复使用 。
- ArrayBlockingQueue存取都采用ReentrantLock加锁,保证线程安全,在多线程环境下也可以放心使用 。
- 使用ArrayBlockingQueue的时候,预估好队列长度,保证生产者和消费者速率相匹配 。
我是「一灯架构」,如果本文对你有帮助 , 欢迎各位小伙伴点赞、评论和关注,感谢各位老铁,我们下期见

文章插图
推荐阅读
- 4 Java注解:一个真实的Elasticsearch案例
- 原生JavaScript
- 3 Java注解:一个真实Elasticsearch案例
- redis bitmap数据结构之java对等操作
- Java工具类 利用Hutool-实现验证码校验
- JavaFx 使用字体图标记录
- 含具体案例 Java8新特性之Stream流
- Java学习之路:流程控制
- UML类中的6种关系
- 五 微软出品自动化神器【Playwright+Java】系列 之常见点击事件操作