文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

JUC的ArrayBlockingQueue怎么实现数据的添加和拿取

2023-06-03 17:26

关注

本篇内容主要讲解“JUC的ArrayBlockingQueue怎么实现数据的添加和拿取”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JUC的ArrayBlockingQueue怎么实现数据的添加和拿取”吧!

ArrayBlockingQueue 有以下几个特点:

ArrayBlockingQueue 跟 LinkedBlockingQueue 一样,同样继承了 AbstractQueue 实现 BlockingQueue 接口,所以在方法上跟 LinkedBlockingQueue 一样,所以在这里我们不把方法列出来了,可以去查看前面 LinkedBlockingQueue 的文章~

除了方法之外,在 ArrayBlockingQueue 中,还有两个比较重要的参数:


// 获取元素的位置
int takeIndex;

// 新增元素时的数组下标
int putIndex;

由于 ArrayBlockingQueue 底层采用的是数组,结合上面的两个参数,ArrayBlockingQueue 的整体结构图大概如下:

JUC的ArrayBlockingQueue怎么实现数据的添加和拿取

ArrayBlockingQueue 有三个构造函数:


public ArrayBlockingQueue(int capacity);
// fair 表示是否为公平锁
public ArrayBlockingQueue(int capacity, boolean fair);

public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c);

关于构造函数就不多说了,都大同小异,跟 LinkedBlockingQueue 一样,同样拿 put()、take() 方法,看看 ArrayBlockingQueue 是如何实现数据的添加和拿取的~

先从put()方法开始,看看 ArrayBlockingQueue 是如何实现的~

public void put(E e) throws InterruptedException {
   Objects.requireNonNull(e);
   // 获取锁
   final ReentrantLock lock = this.lock;
   // 设置可重入锁
   lock.lockInterruptibly();
   try {
       // 当数组队列存满时,阻塞等待.....
       while (count == items.length)
           notFull.await();
       // 入队操作
       enqueue(e);
   } finally {
       // 解锁
       lock.unlock();
   }
}
// 入队  
private void enqueue(E e) {
   final Object[] items = this.items;
   // 根据 putIndex 插入到对应的位置即可
   items[putIndex] = e;
   // 设置好下一次插入的位置,如果当前插入的位置是最后一个元素,
   // 那么下一次插入的位置就是队头了
   if (++putIndex == items.length) putIndex = 0;
   count++;
   notEmpty.signal();
}

put() 方法的实现并不复杂,代码也就 20 行左右,我们来拆解一下 put 过程:

put 方整体解决起来不难,跟 LinkedBlockingQueue 一样,其他添加方法这里就不介绍了,大同小异~

再来看看 ArrayBlockingQueue 是如何实现 take() 方法的,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;
   @SuppressWarnings("unchecked")
   // 根据 takeIndex 获取 items 中的元素
   E e = (E) items[takeIndex];
   // 将 takeIndex 中的数据置为空
   items[takeIndex] = null;
   // 设置下一次获取数据的下标,
   if (++takeIndex == items.length) takeIndex = 0;
   count--;
   if (itrs != null)
       itrs.elementDequeued();
   notFull.signal();
   return e;
}

take() 方法跟 put() 方法没有什么太大的区别,就是一个反操作~

最后我们再来关注一下 remove() 方法,这个方法还是有一些学问的,主要源码如下:


public boolean remove(Object o) {
   if (o == null) return false;
   final ReentrantLock lock = this.lock;
   lock.lock();
   try {
       if (count > 0) {
           final Object[] items = this.items;
           for (int i = takeIndex, end = putIndex,
                    to = (i < end) ? end : items.length;
                ; i = 0, to = end) {
                // 遍历有值的一段数据
               for (; i < to; i++)
                   if (o.equals(items[i])) {
                       removeAt(i);
                       return true;
                   }
               if (to == end) break;
           }
       }
       return false;
   } finally {
       lock.unlock();
   }
}
// 主要看这个方法
void removeAt(final int removeIndex) {
   final Object[] items = this.items;
   // 如果要删除的位置正好是下一次 take的位置
   if (removeIndex == takeIndex) {
       // removing front item; just advance
       items[takeIndex] = null;
       if (++takeIndex == items.length) takeIndex = 0;
       count--;
       if (itrs != null)
           itrs.elementDequeued();
   } else {
       // 如果删除的位置时 takeIndex 和 putIndex 之间的位置,则被删除的数据全部往前移动~
       for (int i = removeIndex, putIndex = this.putIndex;;) {
           int pred = i;
           if (++i == items.length) i = 0;
           if (i == putIndex) {
               items[pred] = null;
               this.putIndex = pred;
               break;
           }
           items[pred] = items[i];
       }
       count--;
       if (itrs != null)
           itrs.removedAt(removeIndex);
   }
   notFull.signal();
}

remove 的时候分三种情况:

到此,相信大家对“JUC的ArrayBlockingQueue怎么实现数据的添加和拿取”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯