FastThreadLocal

前面介绍过 JDK 的 ThreadLocal , 使用不当的话容易造成内存泄漏最终导致OOM, 并且也有一些地方设计的不够好(相对于接下来要介绍的 FastThreadLocal), 接下来我们就介绍一下 Netty 改进的 FastThreadLocal, 看它到底 Fast 在哪里.

(JDK 的 ThreadLocal 的地址: https://www.cnblogs.com/wuhaonan/p/11427119.html)

同样的, 这回我们根据 FastThreadLocal 的源码对其进行分析.

FastThreadLocal#构造方法

FastThreadLocal 有一个标记自己下标的 index , 表明当前 FastThreadLocal 在 InternalThreadLocalMap 存储数据的数组中(Object[] indexedVariables)所处的下标.

    // 位于 map 中的下标private final int index;public FastThreadLocal() {index = InternalThreadLocalMap.nextVariableIndex();}

跟踪 InternalThreadLocalMap.nextVariableIndex(); 的实现可以看到:

    public static int nextVariableIndex() {// nextIndex 见下面int index = nextIndex.getAndIncrement();// 整数的最大值+1就变成了负数, 不过一般也不会用这么多的 ThreadLocalif (index < 0) {nextIndex.decrementAndGet();throw new IllegalStateException("too many thread-local indexed variables");}return index;}// 这是个原子变量, 可以根据这个变量获取当前 FastThreadLocal 下标, 因为这是递增的(nextIndex.getAndIncrement()), 所以不会出现多个 FastThreadLocal 下标相同, 即 FastThreadLocal 的下标唯一.static final AtomicInteger nextIndex = new AtomicInteger();

//todo: 扩容的时候, ThreadLocal 根据 hash 值取余长度计算下标, 可能会导致下标冲突, 需要循环往后查找空的位置放置. FastThreadLocal 直接复制以前的部分, 扩容出来的直接设置初始值, 不用加多一层循环去判断是否为空(可以设置进去), 这就是 唯一的 index 的好处, 不会导致冲突.

FastThreadLocal#set()

    public final void set(V value) {if (value != InternalThreadLocalMap.UNSET) {// 获取当前线程的 threadLocalMapInternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();// 如果是新添加进来的话,则需要注册一个清理器if (setKnownNotUnset(threadLocalMap, value)) {// 注册清理器registerCleaner(threadLocalMap);}} else {remove();}}private boolean setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {// 返回true的话表示是新添加的 ThreadLocalif (threadLocalMap.setIndexedVariable(index, value)) {// 则添加进需要 remove 的 集合(set)中addToVariablesToRemove(threadLocalMap, this);return true;}return false;}// 根据下标设置值, index 为 FastThreadLocal 的 唯一indexpublic boolean setIndexedVariable(int index, Object value) {// 获取到所有存储的 FastThreadLocalObject[] lookup = indexedVariables;// 下标越界判断if (index < lookup.length) {Object oldValue = lookup[index];lookup[index] = value;// 只有添加了新的 ThreadLocal 才会返回 truereturn oldValue == UNSET;} else {// 超过了 map 的大小则进行扩容,扩容见后面的代码expandIndexedVariableTableAndSet(index, value);return true;}} 

可以看到, FastThreadLocal#set 的时候直接根据原子变量获取最新的 index , 然后直接设置进去.

FastThreadLocal#get()

get的过程比较简单,就不多赘述了

    public final V get() {// 当前 thread 的 threadLocalMapInternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();// value 值根据 new 的时候的 index 来获取Object v = threadLocalMap.indexedVariable(index);if (v != InternalThreadLocalMap.UNSET) {return (V) v;}V value = initialize(threadLocalMap);registerCleaner(threadLocalMap);return value;}

InternalThreadLocalMap#expandIndexedVariableTableAndSet

这是 ThreadLocalMap 中的一个扩容方法,一共有3步操作:

1.申请一个新数组,大小为原来的两倍

2.copy数据到新数组(浅拷贝)并且初始化新增部分

3.设置map中新的数组

    //  对 Object[] 进行扩容private void expandIndexedVariableTableAndSet(int index, Object value) {// 旧的 Object[]Object[] oldArray = indexedVariables;final int oldCapacity = oldArray.length;// index * 2int newCapacity = index;newCapacity |= newCapacity >>>  1;newCapacity |= newCapacity >>>  2;newCapacity |= newCapacity >>>  4;newCapacity |= newCapacity >>>  8;newCapacity |= newCapacity >>> 16;newCapacity ++;// 进行浅拷贝Object[] newArray = Arrays.copyOf(oldArray, newCapacity);// 初始化后面新申请的元素 newArray[ oldCapacity , newArray.length )Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);// 设置刚加进来的值newArray[index] = value;// 设置新数组indexedVariables = newArray;}

接下来看一下 JDK 中的 ThreadLocal 中的扩容方法, 也把它当成三步来看吧

1.申请新数组,大小为原来的两倍

2.将数据放入新的数组( hash % (newLen -1))

3.设置map中新的数组

几个步骤看起来是差不多, 主要的不同就是在第二步, JDK 中计算下标的位置是 hash % (newLen -1), 用的是 hash值取余, 会出现冲突, 就像 HashMap 从头节点一直找到链表的最后一个节点(如果是树的话就找到相应大小的地方), 冲突后就循环查找, 这里就是 JDK 的 ThreadLocal 耗时的地方.

  private void resize() {Entry[] oldTab = table;int oldLen = oldTab.length;// double sizeint newLen = oldLen * 2;Entry[] newTab = new Entry[newLen];int count = 0;for (int j = 0; j < oldLen; ++j) {Entry e = oldTab[j];if (e != null) {ThreadLocal<?> k = e.get();if (k == null) {e.value = null; // Help the GC} else {// 这里是数据位于数组中的下标int h = k.threadLocalHashCode & (newLen - 1);// 直到找到空的Entry, 才设置进去, 如果原来的 Entry 已经有了, 需要一直循环往后查找空的位置while (newTab[h] != null)h = nextIndex(h, newLen);newTab[h] = e;count++;}}}setThreshold(newLen);size = count;table = newTab;}

Netty 的 FastThreadLocal 并不会像 JDK 的 ThreadLocal 那样会出现下标冲突和循环里查找, 这是 FastThreadLocal --> Fast 的其中重要原因.

转载于:https://www.cnblogs.com/wuhaonan/p/11565659.html

Netty学习(四)FastThreadLocal相关推荐

  1. Netty学习四:Channel

    1. Channel Channel是Netty的核心概念之一,它是Netty网络通信的主体,由它负责同对端进行网络通信.注册和数据操作等功能. 1.1 工作原理 如上图所示: 一旦用户端连接成功,将 ...

  2. Netty 学习和进阶策略

    https://www.infoq.cn/article/xt9*7K4fJktiuWTLYrZS 背景 Netty 框架的特点 Netty 的一个特点就是入门相对比较容易,但是真正掌握并精通是非常困 ...

  3. Netty学习笔记(六)Pipeline的传播机制

    前面简单提到了下Pipeline的传播机制,这里再详细分析下 Pipeline的传播机制中有两个非常重要的属性inbound和outbound(AbstractChannelHandlerContex ...

  4. Netty学习笔记(二)Netty服务端流程启动分析

    先贴下在NIO和Netty里启动服务端的代码 public class NioServer { /*** 指定端口号启动服务* */public boolean startServer(int por ...

  5. 04、Netty学习笔记—(黏包半包及协议设计解析)

    文章目录 一.粘包与半包 1.1.现象分析 1.1.1.粘包.半包情况分析 1.1.2.滑动窗口.MSS限制.Nagle算法介绍 1.2.粘包.半包现象复现 1.2.1.粘包复现 1.2.2.半包复现 ...

  6. Netty学习(一):初识Netty

    章节 1.Netty学习(一):初识Netty 2.Netty学习(二):Netty的核心组件 3.Netty学习(三):Netty的流程分析 4.Netty学习(四):Netty零拷贝(转载) 5. ...

  7. Netty学习笔记二网络编程

    Netty学习笔记二 二. 网络编程 1. 阻塞模式 阻塞主要表现为: 连接时阻塞 读取数据时阻塞 缺点: 阻塞单线程在没有连接时会阻塞等待连接的到达,连接到了以后,要进行读取数据,如果没有数据,还要 ...

  8. Netty学习笔记一NIO基础

    Netty学习笔记一 一. NIO 基础 non-blocking io 非阻塞IO (也可称为new IO, 因为是JDK1.4加入的) 1. 三大组件 1.1 Channel 通道:数据的传输通道 ...

  9. C#多线程学习(四) 多线程的自动管理(线程池) (转载系列)——继续搜索引擎研究...

    在多线程的程序中,经常会出现两种情况: 一种情况:   应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应                   这一般使用ThreadPo ...

  10. Netty学习笔记(二) 实现服务端和客户端

    在Netty学习笔记(一) 实现DISCARD服务中,我们使用Netty和Python实现了简单的丢弃DISCARD服务,这篇,我们使用Netty实现服务端和客户端交互的需求. 前置工作 开发环境 J ...

最新文章

  1. 3dsMax插件V-Ray建筑可视化三维渲染细节技术学习教程
  2. 谈谈对Spring IOC的理解
  3. mysql 按月自动建表
  4. rabbitmq 集群搭建
  5. string去掉后四位_数字黑洞(下):四位数的黑洞
  6. 科大星云诗社动态20220110
  7. One order event display tool
  8. 杨辉三角Python解法
  9. (2)从实际项目谈起,基于MEF的插件框架之总体设计
  10. git 图形化工具 GitKraken 的使用 —— 分支的创建与合并
  11. windows配置samba客户端_怎样设置Samba文件服务器以使用Windows客户端
  12. sql 基础增删改查语句
  13. heapdump分析工具_.NET Perfview 分析进程性能
  14. 解决开启Vue项目缺少node_models包问题
  15. 汉文博士 0.5.7.2356 版发布
  16. LSH︱python实现局部敏感哈希——LSHash(二)
  17. socket.io实现客户端和服务端的双向通信
  18. EDM营销模式分析和讲解
  19. 我复盘了自己的工作,总结了这个跨境支付产品经理的成长经验给你
  20. 移动互联网创业者遭遇巨头模仿蚕食

热门文章

  1. 安装ssd后不识别网卡_群晖E10M20-T1:你以为它是张网卡,其实它还带俩SSD
  2. KMP算法 next数组 nextval数组
  3. word 代码块_Python+Excel+Word一秒制作百份合同
  4. python os.path.splitext()的用法_Python中的os.path路径模块中的操作方法总结
  5. 华为鸿蒙生态伙伴,华为鸿蒙生态加速 市场相关板块再度活跃
  6. linux开机grub loading,装系统,把grub给装没有了:“grub loading…… grub error 15”
  7. android arraymap排序,内存优化之ArrayMap、SparseArray、SparseIntArray
  8. 甘肃计算机报名准考证打印,2019年9月甘肃计算机等考准考证打印入口已开通
  9. 计算机的概念与发展典型例题,计算机考试基本概念与典型例题.doc
  10. android代码设置digits,android:digits属性