当前位置: 首页 > news >正文

FastThreadLocal 原理解析

FastThreadLocal

fastthreadlocal

  1. 每个 FastThread 包含一个 FastThreadLocalMap,每个 FastThreadLocalThread 中的多个 FastThreadLocal 占用不同的索引。
  2. 每个 InternalThreadLocalMap 的第一个元素保存了所有的 ThreadLocal 对象。之后的元素保存了每个 ThreadLocal 对应的 value

基本操作

get()

当前线程中

  1. 找到 ThreadLocal 的 index,value 加入 InternalThreadLocalMap 对应索引的位置

set()

当前线程中

  1. 找到 ThreadLocal 的 index,value 加入 InternalThreadLocalMap 对应索引的位置
  2. 将 ThreadLocal 加入当前线程 InternalThreadLocalMap 的第一个元素对应的集合

remove()

当前线程中

  1. 找到 ThreadLocal 的 index,InternalThreadLocalMap 中 index 对应的 value 置为 UNSET
  2. 将 ThreadLocal 从当前线程 InternalThreadLocalMap 的第一个元素集合中删除

关键设计点

兼容 ThreadLocal

当线程没有使用 FastThreadLocal 的时候,默认走 ThreadLocal 的逻辑。

初始大小

初始大小为 32

hash 算法

直接使用全局的自增,不存在Hash 冲突,以空间换时间

扩容条件是什么?如何扩容?

  • 扩容条件:当前线程元素超出容量
  • 扩容:元素数量扩展为向上取最近 2 次幂整数,比如,5 取8,31 取 64。
    public boolean setIndexedVariable(int index, Object value) {Object[] lookup = indexedVariables;// index 大于容量if (index < lookup.length) {Object oldValue = lookup[index];lookup[index] = value;return oldValue == UNSET;} else {// 扩容expandIndexedVariableTableAndSet(index, value);return true;}}private void expandIndexedVariableTableAndSet(int index, Object value) {Object[] oldArray = indexedVariables;final int oldCapacity = oldArray.length;int newCapacity;// 当小于 2的30次方时,容量扩展为向上取最近 2 次幂整数,比如,5 取8,31 取 64。if (index < ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD) {newCapacity = index;newCapacity |= newCapacity >>>  1;newCapacity |= newCapacity >>>  2;newCapacity |= newCapacity >>>  4;newCapacity |= newCapacity >>>  8;newCapacity |= newCapacity >>> 16;newCapacity ++;} else {newCapacity = ARRAY_LIST_CAPACITY_MAX_SIZE;}Object[] newArray = Arrays.copyOf(oldArray, newCapacity);Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);newArray[index] = value;indexedVariables = newArray;}

如何防止内存泄漏

自动: 使用ftlt执行一个被FastThreadLocalRunnable wrap的Runnable任务,在任务执行完毕后会自动进行ftl的清理。

final class FastThreadLocalRunnable implements Runnable {private final Runnable runnable;private FastThreadLocalRunnable(Runnable runnable) {this.runnable = ObjectUtil.checkNotNull(runnable, "runnable");}@Overridepublic void run() {try {runnable.run();} finally {FastThreadLocal.removeAll();}}static Runnable wrap(Runnable runnable) {return runnable instanceof FastThreadLocalRunnable ? runnable : new FastThreadLocalRunnable(runnable);}
}

手动: ftl和InternalThreadLocalMap都提供了remove方法,在合适的时候用户可以(有的时候也是必须,例如普通线程的线程池使用ftl)手动进行调用,进行显示删除。

使用

final class FastThreadLocalRunnable implements Runnable {private final Runnable runnable;private FastThreadLocalRunnable(Runnable runnable) {this.runnable = (Runnable)ObjectUtil.checkNotNull(runnable, "runnable");}public void run() {try {this.runnable.run();} finally {// 如果用的是 FastThreadLocalRunnable ,默认会做清理FastThreadLocal.removeAll();}}static Runnable wrap(Runnable runnable) {return (Runnable)(runnable instanceof FastThreadLocalRunnable ? runnable : new FastThreadLocalRunnable(runnable));}
}

存在什么问题

1、空间浪费,所有线程的 ThreadLocalMap 数组大小是一样的
比如,线程1 创建 100 个 ThreadLocal 对象。线程 1 里面有一个长度为 100 的数组。
此时,第二个线程需要调用 ThreadLocal 100 的 get 方法,第二个线程需要分配 100 个 Object 对象。

import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.concurrent.FastThreadLocalThread;import java.util.ArrayList;
import java.util.List;public class FastThreadLocalTest {private List<FastThreadLocal<String>> fastThreadLocals = new ArrayList<>();private List<ThreadLocal<String>> threadLocals = new ArrayList<>();void thread1Init() {new Thread (() -> {for (int i = 0; i < 31; i++) {ThreadLocal<String> threadLocal = new ThreadLocal<>();threadLocal.get();threadLocals.add(threadLocal);}}).start();}void thread2Init() {new Thread(() -> {threadLocals.get(threadLocals.size() - 1).get();});}void fastThread1Init() {new FastThreadLocalThread (() -> {for (int i = 0; i < 33; i++) {FastThreadLocal<String> fastThreadLocal = new FastThreadLocal<>();fastThreadLocal.get();fastThreadLocals.add(fastThreadLocal);}}).start();}void fastThread2Init() {new FastThreadLocalThread(() -> {fastThreadLocals.get(fastThreadLocals.size() - 1).get();});}public static void main(String[] args) {FastThreadLocalTest test = new FastThreadLocalTest();test.fastThread1Init();test.fastThread2Init();test.thread1Init();test.thread2Init();}
}

2、FastThreadLocal 需要配套 FastThreadLocalThread 使用,不然还不如原生 ThreadLocal。
3、FastThreadLocal 使用最好配套 FastThreadLocalRunnable,这样执行完任务后会主动调用 removeAll 来移除所有

性能压测

netty 官方 mincrobench

http://www.lryc.cn/news/90955.html

相关文章:

  • 设计模式B站学习(一)(java)
  • Pandas如何轻松按位置删除多重索引列?
  • 第五十七天学习记录:C语言进阶:结构体链表的自学
  • 【一次调频】考虑储能电池参与一次调频技术经济模型的容量配置方法(Matlab代码实现)
  • ICV报告: 智能座舱SoC全球市场规模预计2025年突破50亿美元
  • 在can协议的基础下编写DBC文件,然后使用该DBC文件下发can协议到底盘完整流程
  • 工业传感器有哪些?
  • Docker应用部署之Nginx
  • TerminalWorks TSPrint/TSScan/TSWebCam Crack
  • 如何使用Springboot实现文件上传和下载,并为其添加实时进度条的功能
  • 安装并新建windows下wxwroks7.0 bootrom工程
  • element-ui表格el-table的使用
  • Backtrader官方中文文档:第八章Indicators指标
  • CAP原则
  • 【PowerQuery】M语言的使用产品和使用场景
  • 【Linux】遇事不决,可先点灯,LED驱动的进化之路---1
  • hive任务reduce步骤卡在99%原因及解决
  • C++11 -- lambda表达式
  • 【开源项目】银行查询服务的设计和实现
  • Linux服务器禁止密码登录,设置秘钥登录
  • 第十八章 开发Productions - ObjectScript Productions - 通过引用或作为输出传递值
  • 【云原生|Kubernetes】07-Pod健康检查和服务可用性检查
  • jeecgboot使用的问题记录
  • 【C++】数组 - 一维数组,二维数组
  • 前端:使用rollup的简单记录
  • 基于flask的web应用开发——接受post请求
  • Linux源码包的安装与升级
  • 电子合同签署协议开源版系统开发
  • 【每日一题Day221】LC2455可被三整除的偶数的平均值 | 模拟
  • NCI架构-1