本文的github地址:点此

该文所涉及的netty源码版本为4.1.6。

MpscLinkedQueue是什么

在Netty的核心中的核心成员NioEventLoop中,其中任务队列的实现taskQueue便是MpscLinkedQueue。MpscLinkedQueue是Netty所实现的一个基于多生产者单消费者的无锁队列,针对NioEventLoop中任务队列的特点,其单消费者的场景在一开始就避免了从队列中取数据时加锁的必要,而其最精妙的地方便是在多生产者并发从队列中添加数据的时候也没有加锁,达到Netty所期望的高性能实现。这是如何实现的?

MpscLinkedQueue无锁并发线程安全写入原理

MpscLinkedQueue对于尾结点的维护

首先,MpscLinkedQueue继承自AtomicReference,也就是说MpscLinkedQueue通过继承自AtomicReference的方式,显式地维护了一个提供原子读写能力的变量value。而在MpscLinkedQueue中,这个value是其内部维护的队列的尾结点。

MpscLinkedQueue对于头结点的维护

而后,来看MpscLinkedQueue的构造方法。

    MpscLinkedQueue() {MpscLinkedQueueNode<E> tombstone = new DefaultNode<E>(null);headRef = new FullyPaddedReference<MpscLinkedQueueNode<E>>();headRef.set(tombstone);setTail(tombstone);}

在MpscLinkedQueue中,维护着headRef头结点字段,其队列内部节点的实现是一个MpscLinkedQueueNode。MpscLinkedQueueNode是一个除了存放具体队列元素外只有next字段的节点,也就是说,MpscLinkedQueue的队列是单向的。在构造方法的最后,通过setTail()方法的,将MpscLinkedQueue的尾结点字段value也设置为头结点。MpscLinkedQueue的头结点字段headRef的存在可以方便后续直接从头结点开始的队列操作,消费者可以简单判断头尾节点是否相等来确认队列中是否有元素可以消费。

MpscLinkedQueue如何做到线程安全的无锁加入

    @Override@SuppressWarnings("unchecked")public boolean offer(E value) {if (value == null) {throw new NullPointerException("value");}final MpscLinkedQueueNode<E> newTail;if (value instanceof MpscLinkedQueueNode) {newTail = (MpscLinkedQueueNode<E>) value;newTail.setNext(null);} else {newTail = new DefaultNode<E>(value);}MpscLinkedQueueNode<E> oldTail = replaceTail(newTail);oldTail.setNext(newTail);return true;}private MpscLinkedQueueNode<E> replaceTail(MpscLinkedQueueNode<E> node) {return getAndSet(node);}

MpscLinkedQueue的offer()方法很简短,但是恰恰就是整个添加队列元素加入的流程,当元素被加入的时候,首先判断加入的元素是否是MpscLinkedQueueNode,如果不是则进行封装。之后便是整个操作的重点:

  • 通过replaceTail()方法,将当前被加入的节点通过AtomicReference所提供的getAndSet()方法将其设为队列的尾结点,并返回先前的尾结点。这次操作由UNSAFE的CAS来保证操作的原子性。
  • 之后将之前的尾结点的next指向新加入的节点,本次加入宣告结束。
    整个操作就到此结束,这里可以看出,MpscLinkedQueue利用了AtomicReference底层UNSAFE的能力,通过CAS确保新设置进入value的节点必定能够和原先的节点达成一个且唯一的联系,那么只需要自顶向下不断通过将这个联系变成引用,那么一条队列便形成了。由于其实现是链表而不是数组,也就没有涉及到资源的竞争,在不加锁的前提下其队列顺序可能不会严格按照加入顺序,但这在当前场景下并不是问题。在这个前提,高并发的插入场景下,每个新进入的新节点都将获取原尾位置value上的节点,而自身将会被设置为其后驱节点重新放到尾结点位置上,CAS在不加锁的前提下保证了前后节点对应关系的唯一性,完成了并发条件下不加锁的线程安全写入。

MpscLinkedQueue不支持remove()

在MpscLinkedQueue中,是不支持remove()的方法去从队列中移除任意一个元素的。原因很简单,消费者和生产者是无锁的,消费者可以通过比较队首和队尾元素是否一致来保证线程安全地从队首取数据,但是remove()从队列中任意位置修改数据是线程不安全的,主要体现在移除队尾元素可能会导致正在加入的新元素被丢弃。

MpscLinkedQueue另外的实现细节

  • MpscLinkedQueue中的头节点被通过FullyPaddedReference封装。其内部前后分别填充56字节和64字节来进行填充以避免伪共享导致的性能损耗,使得其头结点可以高效被访问。关于伪共享的相关知识可以通过搜索引擎进行查询。
  • MpscLinkedQueue在消费者消费数据后,当将下一个节点设置为头结点的时候,并不是直接进行赋值,而是通过UNSAFE来根据偏移量赋值,这样做将略微提高性能,主要是内存屏障storestrore和loadstrore之间的性能差异。

Netty技术细节源码分析-MpscLinkedQueue队列原理分析相关推荐

  1. 老李推荐:第6章6节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令队列...

    老李推荐:第6章6节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令队列 事件源在获得字串命令并把它翻译成对应的MonkeyEvent事件后,会把这些事件 ...

  2. 老李推荐:第5章5节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 获取系统服务引用 1...

    老李推荐:第5章5节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 获取系统服务引用 上一节我们描述了monkey的命令处理入口函数run是如何调用optionPro ...

  3. 老李推荐:第6章1节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览 1...

    老李推荐:第6章1节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览 在上一章中我们有简要的介绍了事件源是怎么一回事,但是并没有进行详细的描述.那么往下的这几个 ...

  4. 顺序线性表 ---- ArrayList 源码解析及实现原理分析

    原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7738888.html ------------------------------------ ...

  5. Netty技术细节源码分析-Recycler对象池原理分析

    本文是该篇的修正版 本文的github地址:点此 该文所涉及的netty源码版本为4.1.6. Netty的对象池Recycler是什么 Recycler是Netty中基于ThreadLocal的轻量 ...

  6. Netty技术细节源码分析-HashedWheelTimer时间轮原理分析

    本文是该篇的修正版 本文的github地址:点此 该文所涉及的netty源码版本为4.1.6. Netty时间轮HashedWheelTimer是什么 Netty的时间轮HashedWheelTime ...

  7. Netty技术细节源码分析-ByteBuf的内存泄漏原因与检测

    本文的github地址:点此 该文所涉及的netty源码版本为4.1.6. Netty中的ByteBuf为什么会发生内存泄漏 在Netty中,ByetBuf并不是只采用可达性分析来对ByteBuf底层 ...

  8. Netty技术细节源码分析-FastThreadLocal源码分析

    本文是该篇的修正版 本文的github地址:点此 Netty 的 FastThreadLocal 源码解析 该文中涉及到的 Netty 源码版本为 4.1.6. Netty 的 FastThreadL ...

  9. Netty技术细节源码分析-内存池之PoolChunk设计与实现

    该文所涉及的netty源码版本为4.1.16. 在一开始需要明确的几个概念 在Netty的内存池的PoolChunk中,先要明确以下几个概念. page: page是chunk中所能申请到的最小内存单 ...

最新文章

  1. Depends, python2.7-minimal (= 2.7.15_rc1-1) 问题解决方法使用 aptitude 安装以及与 apt-get 的区别
  2. print、println的区别
  3. 纪念逝去的头发--一次debug经历
  4. 夏日炎炎,请照顾好你的电脑
  5. 华为手机记事本导出_深夜浅谈怎样用一部手机做电影解说?
  6. WSS(Windows Storage Server)2008R2使用指南(三)配置及使用篇
  7. 降序排序_排序简单,应用不易,使用Excel排序的几点建议
  8. 67% 为宽松许可证,2020 年开源许可证最新趋势来袭
  9. Bambook 简介
  10. java机房收费管理系统课程设计_(c语言)机房收费管理系统课程设计
  11. Axure教程-苹果X母版制作
  12. Android 百度地图定位
  13. pdf编辑器工具哪个好?好用的pdf编辑器一款就够!
  14. 软考成绩什么时候出?
  15. HTML实现简单的注册页面
  16. 算法初步 计算机程序,算法初步-程序框图
  17. 数据库oracle审计,Oracle数据库审计策略
  18. 做嵌入式开发,前景怎么样?
  19. Ubuntu16.04搭建OpenGrok环境
  20. Linux启动时显示Grub命令行及修改

热门文章

  1. Linux 自动化运维工具 ansible
  2. Resx 文件无效。未能加载 .RESX 文件中使用的类型 System.Collections.Generic.List`1请确保已在项目中添加了必需的引用。
  3. C#利用Microsoft.Office.Interop.Excel导出数据到Excel
  4. 设置mysql8的root可以远程访问
  5. 【SpringSecurity系列02】SpringSecurity 表单认证逻辑源码解读
  6. 没有桌面体验功能就不能进行图片打印
  7. openresty + nginx-http-sysguard 调研使用
  8. 写给90后快30岁的我们
  9. 百度之星资格赛,hdu 4825 XOR SUM
  10. Entity Framework 4 in Action读书笔记——第四章:使用LINQ to Entities查询:使用函数...