java中怎么利用ConcurrentLinkedQueue实现并发

发布时间:2021-06-21 16:50:32 作者:Leah
来源:亿速云 阅读:202

本篇文章给大家分享的是有关java中怎么利用ConcurrentLinkedQueue实现并发,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

在并发编程中,我们可能经常需要用到线程安全的队列,java为此提供了两种模式的队列:阻塞队列和非阻塞队列。

:阻塞队列和非阻塞队列如何实现线程安全?

接下来我们就来看看JDK是如何使用非阻塞的方式来实现线程安全队列ConcurrentLinkedQueue的。

ConcurrentLinkedQueue源码分析

ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,遵循队列的FIFO原则,队尾入队,队首出队。我们先来看下队列的基础数据结构以及初始化相关源码实现。

队列基础数据结构Node及初始化

看完数据结构及初始化后,接下里我们就该来看看队列的两个重要实现:入队和出队。

ConcurrentLinkedQueue入队

offer实现

入队的实质就是在队尾做节点插入,具体的执行流程如下:

  1. 调用checkNotNull方法判断待入队元素是否为null,如果为null则抛出空指针异常;

  2. 创建一个待入队节点;

  3. 循环执行队尾插入:

到这里为止,入队的源码分析差不多,说实在的,还是很懵,大家心里可能可能还在纠结,第一,入队的过程到底是什么样子的呀?第二,入队直接CAS更新tail节点就可以了,为什么还要那么费劲的分情况处理?

针对第一个问题,给出一个图,大家就能完全明白了:

队列入队结构变化图.jpg

  1. 添加节点1,此时tail节点的next节点为null,符合上述代码中的情况1,更新队列的tail节点的next节点为元素1,由于初始化是head节点等于tail节点,所以此时head和tail的next节点均指向节点1;

  2. 添加节点2,此时tail节点的next节点不为null,同时p也不等于q,符合上述代码中的情况3,首先执行情况3将p指向tail节点的next节点,再执行情况1相关逻辑,设置节点1的next节点为元素2,此时p不等于t,更新tail节点,将tail节点指向元素2;

节点3和4入队过程与1和2入队一致,在这里我就不再做赘述。需要注意的是:tail节点并不一定是指向队列的最后一个节点,它可能指向最后一个节点的前一个节点!!!

针对第二个问题,我们来尝试换一种方式思考,假如我们每次就让tail节点作为队尾节点,每次的入队所要做的事情其实就是将入队节点设置成尾节点,代码可以简化成这样:

tail为队尾节点的入队


上述代码量确实非常少,而且逻辑非常清楚和易懂,但是这样做有个缺点就是每次入队都需要循环CAS更新tail节点。
如果能减少更新tail节点的次数,那么就能提高入队的效率,所以,Doug Lea并没有让tail节点作为队尾节点,只有tail节点与队尾节点之间的距离等于1的时候才需要更新tail节点。但是,这样就可能导致当队列长度越长的时候每次入队定位尾节点的时间就会越长,即便是这样,它仍然可以提高入队效率,因为从本质上来看,volatile变量的写操作的开销要远远大于读操作的。

分析完入队,接下来我们来看看ConcurrentLinkedQueue的出队。

ConcurrentLinkedQueue出队

poll实现

出队的实质就是情况表头节点的引用并返回表头节点的值,具体的之行逻辑如下:

  1. 获取头结点的元素;

  2. 如果表头节点的元素不为null,并且调用p.casItem(item, null)设置表头节点数据为null成功:

  3. 如果步骤2条件不成立并且表头节点的next节点q为null,那么此时队列只有一个为null的节点,调用updateHead方法更新表头节点为p,然后返回null;

  4. 如果步骤2和3的条件均不成立并且p等于q,跳转到restartFromHead标记重新执行;

  5. 步骤2,3,4均不成立,将p指向q;

  6. 循环执行上述步骤;

源码其实就这么多,为了方便理解出队的过程,还是照样给一个图:

队列出队结构变化图

  1. 节点1出队,此时head的item为null,执行上述代码逻辑中的步骤5,p指向节点1,此时p的item域不为空,执行步骤2,将节点1的item设置为null,此时p不等于h,更新头结点(如果p的next节点不为null,将头结点指向q,否则指向p),返回节点1的item,head执行节点2;

  2. 节点2出队,此时head的item不为null,执行上述代码逻辑中步骤2,将节点2的item设置为null,此时p等于h,直接返回节点2的item,head仍然指向节点2;

节点3和4出队过程与1和2出队一致,在这里我就不再做赘述。需要注意的是head节点并不一定是指向队列的第一个有效节点,它可能指向有效节点的前一个节点!!!

注:这里的有效节点是指从head节点向后遍历可达的节点当中,item不为null的节点。

当然,为什么head节点不总是指向队列的第一个有效节点,其原因跟入队是一样的,这么做的最主要也是减少CAS更新head节点的次数,从而提高出队效率。

以上就是java中怎么利用ConcurrentLinkedQueue实现并发,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注亿速云行业资讯频道。

推荐阅读:
  1. 并发容器之ConcurrentLinkedQueue
  2. Java中怎么利用多线程实现并发编程

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java concurrentlinkedqueue

上一篇:tomcat8中怎么绑定两个https域名

下一篇:dubbo中DubboHealthIndicator的作用是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》