范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

学习一波Netty封装的FastThreadLocal

  1 FastThreadLocal的引入背景和原理简介
  既然jdk已经有ThreadLocal,为何netty还要自己造个FastThreadLocal?FastThreadLocal快在哪里?
  这需要从jdk ThreadLocal的本身说起。如下图:
  在java线程中,每个线程都有一个ThreadLocalMap实例变量(如果不使用ThreadLocal,不会创建这个Map,一个线程第一次访问某个ThreadLocal变量时,才会创建)。
  该Map是使用线性探测的方式解决hash冲突的问题,如果没有找到空闲的slot,就不断往后尝试,直到找到一个空闲的位置,插入entry,这种方式在经常遇到hash冲突时,影响效率。
  FastThreadLocal(下文简称ftl)直接使用数组避免了hash冲突的发生,具体做法是:每一个FastThreadLocal实例创建时,分配一个下标index;分配index使用AtomicInteger实现,每个FastThreadLocal都能获取到一个不重复的下标。
  当调用 ftl.get() 方法获取值时,直接从数组获取返回,如return array[index] ,如下图:
  2 实现源码分析
  根据上文图示可知,ftl的实现,涉及到InternalThreadLocalMap、FastThreadLocalThread和FastThreadLocal几个类,自底向上,我们先从InternalThreadLocalMap开始分析。
  InternalThreadLocalMap类的继承关系图如下:
  2.1 UnpaddedInternalThreadLocalMap的主要属性static final ThreadLocal slowThreadLocalMap = new ThreadLocal(); static final AtomicInteger nextIndex = new AtomicInteger(); Object[] indexedVariables;
  数组indexedVariables就是用来存储ftl的value的,使用下标的方式直接访问。nextIndex在ftl实例创建时用来给每个ftl实例分配一个下标,slowThreadLocalMap在线程不是ftlt时使用到。  2.2 InternalThreadLocalMap分析
  InternalThreadLocalMap的主要属性:  // 用于标识数组的槽位还未使用 public static final Object UNSET = new Object(); /**  * 用于标识ftl变量是否注册了cleaner  * BitSet简要原理:  * BitSet默认底层数据结构是一个long[]数组,开始时长度为1,即只有long[0],而一个long有64bit。  * 当BitSet.set(1)的时候,表示将long[0]的第二位设置为true,即0000 0000 ... 0010(64bit),则long[0]==2  * 当BitSet.get(1)的时候,第二位为1,则表示true;如果是0,则表示false  * 当BitSet.set(64)的时候,表示设置第65位,此时long[0]已经不够用了,扩容处long[1]来,进行存储  *  * 存储类似 {index:boolean} 键值对,用于防止一个FastThreadLocal多次启动清理线程  * 将index位置的bit设为true,表示该InternalThreadLocalMap中对该FastThreadLocal已经启动了清理线程  */ private BitSet cleanerFlags;  private InternalThreadLocalMap() {         super(newIndexedVariableTable()); }  private static Object[] newIndexedVariableTable() {         Object[] array = new Object[32];         Arrays.fill(array, UNSET);         return array; }
  比较简单, newIndexedVariableTable() 方法创建长度为32的数组,然后初始化为UNSET,然后传给父类。之后ftl的值就保存到这个数组里面。
  注意,这里保存的直接是变量值,不是entry,这是和jdk ThreadLocal不同的。InternalThreadLocalMap就先分析到这,其他方法在后面分析ftl再具体说。   2.3 ftlt的实现分析
  要发挥ftl的性能优势,必须和ftlt结合使用,否则就会退化到jdk的ThreadLocal。ftlt比较简单,关键代码如下:  public class FastThreadLocalThread extends Thread {   // This will be set to true if we have a chance to wrap the Runnable.   private final boolean cleanupFastThreadLocals;      private InternalThreadLocalMap threadLocalMap;      public final InternalThreadLocalMap threadLocalMap() {         return threadLocalMap;   }   public final void setThreadLocalMap(InternalThreadLocalMap threadLocalMap) {         this.threadLocalMap = threadLocalMap;   } }
  ftlt的诀窍就在threadLocalMap属性,它继承java Thread,然后聚合了自己的InternalThreadLocalMap。后面访问ftl变量,对于ftlt线程,都直接从InternalThreadLocalMap获取变量值。  2.4 ftl实现分析
  ftl实现分析基于netty-4.1.34版本,特别地声明了版本,是因为在清除的地方,该版本的源码已经注释掉了ObjectCleaner的调用,和之前的版本有所不同。  2.4.1 ftl的属性和实例化private final int index;  public FastThreadLocal() {     index = InternalThreadLocalMap.nextVariableIndex(); }
  非常简单,就是给属性index赋值,赋值的静态方法在InternalThreadLocalMap:   public static int nextVariableIndex() {         int index = nextIndex.getAndIncrement();         if (index < 0) {             nextIndex.decrementAndGet();             throw new IllegalStateException("too many thread-local indexed variables");         }         return index;   }
  可见,每个ftl实例以步长为1的递增序列,获取index值,这保证了InternalThreadLocalMap中数组的长度不会突增。  2.4.2 get()方法实现分析public final V get() {     InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get(); // 1     Object v = threadLocalMap.indexedVariable(index); // 2     if (v != InternalThreadLocalMap.UNSET) {         return (V) v;     }      V value = initialize(threadLocalMap); // 3     registerCleaner(threadLocalMap);  // 4     return value; }
  1.先来看看 InternalThreadLocalMap.get() 方法如何获取threadLocalMap: =======================InternalThreadLocalMap=======================     public static InternalThreadLocalMap get() {         Thread thread = Thread.currentThread();         if (thread instanceof FastThreadLocalThread) {             return fastGet((FastThreadLocalThread) thread);         } else {             return slowGet();         }     }        private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {         InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();         if (threadLocalMap == null) {             thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());         }         return threadLocalMap;     }
  因为结合FastThreadLocalThread使用才能发挥FastThreadLocal的性能优势,所以主要看fastGet方法。该方法直接从ftlt线程获取threadLocalMap,还没有则创建一个InternalThreadLocalMap实例并设置进去,然后返回。
  2. threadLocalMap.indexedVariable(index) 就简单了,直接从数组获取值,然后返回:   public Object indexedVariable(int index) {         Object[] lookup = indexedVariables;         return index < lookup.length? lookup[index] : UNSET;     }
  3.如果获取到的值不是UNSET,那么是个有效的值,直接返回。如果是UNSET,则初始化。
  initialize(threadLocalMap) 方法:   private V initialize(InternalThreadLocalMap threadLocalMap) {         V v = null;         try {             v = initialValue();         } catch (Exception e) {             PlatformDependent.throwException(e);         }          threadLocalMap.setIndexedVariable(index, v); // 3-1         addToVariablesToRemove(threadLocalMap, this); // 3-2         return v;     }
  3.1.获取ftl的初始值,然后保存到ftl里的数组,如果数组长度不够则扩充数组长度,然后保存,不展开。
  3.2. addToVariablesToRemove(threadLocalMap, this) 的实现,是将ftl实例保存在threadLocalMap内部数组第0个元素的Set集合中。
  此处不贴代码,用图示如下:
  4. registerCleaner(threadLocalMap) 的实现,netty-4.1.34版本中的源码: private void registerCleaner(final InternalThreadLocalMap threadLocalMap) {         Thread current = Thread.currentThread();         if (FastThreadLocalThread.willCleanupFastThreadLocals(current) || threadLocalMap.isCleanerFlagSet(index)) {             return;         }          threadLocalMap.setCleanerFlag(index);          // TODO: We need to find a better way to handle this.         /*         // We will need to ensure we will trigger remove(InternalThreadLocalMap) so everything will be released         // and FastThreadLocal.onRemoval(...) will be called.         ObjectCleaner.register(current, new Runnable() {             @Override             public void run() {                 remove(threadLocalMap);                  // It"s fine to not call InternalThreadLocalMap.remove() here as this will only be triggered once                 // the Thread is collected by GC. In this case the ThreadLocal will be gone away already.             }         });         */ }
  由于ObjectCleaner.register这段代码在该版本已经注释掉,而余下逻辑比较简单,因此不再做分析。  2.5 普通线程使用ftl的性能退化
  随着 get() 方法分析完毕,set(value) 方法原理也呼之欲出,限于篇幅,不再单独分析。
  前文说过,ftl要结合ftlt才能最大地发挥其性能,如果是其他的普通线程,就会退化到jdk的ThreadLocal的情况,因为普通线程没有包含InternalThreadLocalMap这样的数据结构,接下来我们看如何退化。学习资料:Java进阶视频资源
  从InternalThreadLocalMap的 get() 方法看起: =======================InternalThreadLocalMap=======================     public static InternalThreadLocalMap get() {         Thread thread = Thread.currentThread();         if (thread instanceof FastThreadLocalThread) {             return fastGet((FastThreadLocalThread) thread);         } else {             return slowGet();         }     }    private static InternalThreadLocalMap slowGet() {        // 父类的类型为jdk ThreadLocald的静态属性,从该threadLocal获取InternalThreadLocalMap         ThreadLocal slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;         InternalThreadLocalMap ret = slowThreadLocalMap.get();         if (ret == null) {             ret = new InternalThreadLocalMap();             slowThreadLocalMap.set(ret);         }         return ret;     }
  从ftl看,退化操作的整个流程是:从一个jdk的ThreadLocal变量中获取InternalThreadLocalMap,然后再从InternalThreadLocalMap获取指定数组下标的值,对象关系示意图:
  3 ftl的资源回收机制
  在netty中对于ftl提供了三种回收机制:
  自动:  使用ftlt执行一个被FastThreadLocalRunnable wrap的Runnable任务,在任务执行完毕后会自动进行ftl的清理。
  手动:  ftl和InternalThreadLocalMap都提供了remove方法,在合适的时候用户可以(有的时候也是必须,例如普通线程的线程池使用ftl)手动进行调用,进行显示删除。
  自动:  为当前线程的每一个ftl注册一个Cleaner,当线程对象不强可达的时候,该Cleaner线程会将当前线程的当前ftl进行回收。(netty推荐如果可以用其他两种方式,就不要再用这种方式,因为需要另起线程,耗费资源,而且多线程就会造成一些资源竞争,在netty-4.1.34版本中,已经注释掉了调用ObjectCleaner的代码。)  4 ftl在netty中的使用
  ftl在netty中最重要的使用,就是分配ByteBuf。基本做法是:每个线程都分配一块内存(PoolArena),当需要分配ByteBuf时,线程先从自己持有的PoolArena分配,如果自己无法分配,再采用全局分配。
  但是由于内存资源有限,所以还是会有多个线程持有同一块PoolArena的情况。不过这种方式已经最大限度地减轻了多线程的资源竞争,提高程序效率。
  具体的代码在 PoolByteBufAllocator的内部类PoolThreadLocalCache中:    final class PoolThreadLocalCache extends FastThreadLocal {      @Override         protected synchronized PoolThreadCache initialValue() {             final PoolArena heapArena = leastUsedArena(heapArenas);             final PoolArena directArena = leastUsedArena(directArenas);              Thread current = Thread.currentThread();             if (useCacheForAllThreads || current instanceof FastThreadLocalThread) {               // PoolThreadCache即为各个线程持有的内存块的封装                 return new PoolThreadCache(                         heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize,                         DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);             }             // No caching so just use 0 as sizes.             return new PoolThreadCache(heapArena, directArena, 0, 0, 0, 0, 0);         }     }
  参考资料  Netty源码分析3 - FastThreadLocal 框架的设计  Netty进阶:自顶向下解析FastThreadLocal  来源:blog.csdn.net/mycs2012/article/details/90898128

S19最强战神确认,飞起来之后,典韦转身跑,铠皇直接跪本文只代表个人观点前言要问S19赛季谁才是最强战神?我想很多人都会说是典韦或是铠皇,因为他们都有着很强的秒杀能力。咱们先说典韦,这个英雄真的可以说是一个杀神,大招命中对方之后,每一吕布选择攻速暴击,还是半肉出装?大神告诉你这样才能攻防兼备本文只代表个人观点前言吕布是王者荣耀之中比较热门的英雄,虽然他的胜率不是很高,但是很多人都对他情有独钟。原因就是吕布本身带有百分之百的真实伤害,可能前期看不出什么,但是到了后期就算后羿怕阿轲,伽罗怕兰陵王,这个射手谁都不怕,猴子都能反杀本文只代表个人观点前言在王者荣耀之中,刺客英雄是非常克制射手的。像是后羿只要见到阿轲几乎就是被秒杀的下场,而伽罗遇到兰陵王也没有什么还手之力。那么有没有射手不害怕刺客呢?其实还真就泰拉瑞亚手游这么豪横?端游玩家等了几年的功能,手游上了泰拉瑞亚手游正式上线taptap了,这一消息还是挺振奋人心的,尤其是因为工作原因或者其他原因不得不放弃泰拉瑞亚端游的玩家们开心不已,以后就可以随时随地用手机体验泰拉瑞亚。而且手游上泰拉瑞亚国服手机版来袭!经典老番终于能随时随地玩了前言说起知名沙盒游戏泰拉瑞亚,相信各位小伙伴们一定不陌生。毕竟早在2011年的时候,泰拉瑞亚就正式在PC端发行了,而后凭借着自由度高玩法多样化等特点,吸引了无数玩家。在2020年的泰拉瑞亚的月亮领主也太难打了吧!这boss设计的真离谱泰拉瑞亚这款游戏,不知道有多少小伙伴对他比较熟悉呢?作为一款十年的老游戏,虽然在这些年一直都处于不温不火的状态,但是喜欢这款游戏的玩家依旧有非常多。泰拉瑞亚是一款2d沙盒游戏,说起18年经典武侠回归,剑网1归来手游霸屏抖音金秋九月,由西山居与盛趣游戏联合发行的剑网1归来手游正式曝光。在游戏仅开启官网预约活动以及iOS预订的情况下,游戏相关视频却在抖音频繁露脸,并成为众多游戏主播争先直播的宠儿。或许不萌妹语音包再度返场,若若学烫嘴歌曲,菠萝赛冬收到神秘礼物为了能够拉近主播和玩家的距离,和平精英之前就上线了一些人气主播的语音包。而在这么多主播的语音包中,若若跑的贼快的语音包受到了玩家的喜爱。毕竟若若作为和平精英板块少有的萌妹主播,凭借印度买航母结果被坑,Steam海盗游戏ATLAS老外被耍得团团转众所周知,印度是一个非常神奇的国家,经常会搞些大新闻出来。在印度军事发展史上,也发生过不少有趣的事情。印度向俄罗斯买航母的事情应该很多人都知道。俄罗斯曾经给印度送了一个大大的馅饼,推荐10款好玩的Steam游戏,闲余时间收获满满的幸福感在Steam上,现在出现了很多玩法出色情节丰富的作品,今天我就来给大家总结10款这类游戏,它们品质很好,能够在闲暇时间里给玩家们带去满满的幸福感。方舟生存进化方舟生存进化是Stea10款好玩的Steam游戏盘点,沙盒求生射击应有尽有每一个玩家都有他们的偏爱与喜好,为了满足这些玩家,游戏圈内推出了很多种游戏类型,有沙盒求生射击等等,下面我就来给大家总结10款好玩的Steam游戏,它们几乎涵盖了所有的游戏类型,相
高性价比的8天线wifi6路由器,锐捷星耀X32体验无论是办公还是家居生活肯定少不了路由器这款产品,更好的路由器款式可以为我们带来更为稳定高效的网络连接品质。随着越来越多的新款手机都走入了WIFI6的新时代,那么选择一款信号优秀,性新游测评百战天虫大混战没了回合制还是那个味吗?AS玩家百战天虫大混战是时隔上代作品百战天虫战争武器4年,在12月1号刚刚上架steam的新游戏,售价62元RMB。这代取消了以往的回合制模式,变成了即时战斗大混战,我自己试玩了一段时间游量化分析生物医药行业梳理年化收益率盘点(截止21。9。30)截止2021。9。30,国内生物医药行业上市公司399家,今天一起来树理一下这399家公司的年化收益率的情况。一2021年上市公司2021年1月1日截止到9。30,生物医药行业新上量化分析三年ROIC超15,四季度净利润增速每股基本收益超20今天通过ROIC,连续4个季度净利润增速,每股收益增速指标来筛选下比较优秀的公司筛选指标1)市值200亿以上2)连续3年ROIC大于15,取2018年,2019年,2020年数据3大师段位不会降级吗?宝可梦大集结出现反派高手玩家宝可梦大集结并不简单你好,这里是Holdon2099的宝可梦世界,宝可梦大集结已经发布了一段时间,也有不少玩家通过个人实力或者组队配合到达了大师段位,但大家发现一个问题,就是当你在王者里特别克制小短腿类英雄的法师他,踏空而行,是一位斗宗强者,额不对不对,是一位具备王者之气的英雄,一出手就势不可挡,一招技能无人能挡,对方英雄如果是没位移的小短腿了,那这局基本上己方能占60的胜率了,最重要的是王者里前期升级快,后期大招秒半血的英雄王者荣耀里面有一个英雄。在前期对队友的加成属性非常高,如果你要打滚雪球式的压制胜利,那么这个英雄一定不会让你失望。他的经验增益是王者里其他英雄所没有的,当然,如果前期己方发展不顺利荣耀平板V7Pro5G版上市!这三大创新技术您会喜欢吗?10月15日1008来自荣耀品牌的新款平板V7Pro5G版全面开售。从前面发布的荣耀平板V7Pro版本就可以堪称,它颇具潮流的外观设计与出色的生产力表现备受消费者关注,其中晨晖金配冷漠先生力所能及的帮助,让善意更好的传递帮助他人,并非是要从这件事情里获得什么,因为帮助本身就是一种快乐。动画短片冷漠先生讲述的是一个人从极度冷漠到善良热心的转变,这种性格的转变需要我们更加深入地思考。从冷漠到热心的转变喜羊羊之筐出胜利官方拟人,红太狼不显老,被沸羊羊帅到了前不久,喜羊羊与灰太狼之决战次时代官方整活,不仅推出了羊村羊羊们长大之后的样子,还为他们画了相应的拟人形象。这种新鲜活力的注入,在网友中间引起了相当高的讨论热度。以运动为题材开展的JOJO的奇妙冒险第六部动画,采取月更播放?将于12月播出近期关于JOJO的奇妙冒险第六部动画化的消息越来越多,人设图和宣传海报都已相继制作完毕。有前几季人气的加持,想必第六部引起的期待也不小。根据JOJO动画系列官方发布的最新消息,第六