[Netty] FastThreadLocal (十四)

news2024/10/5 21:23:36

文章目录

      • 1.FastThreadLocal介绍
      • 2.FastThreadLocal分析
      • 3.FastThreadLocal结构分析
      • 4.FastThreadLocal方法分析
        • 4.1 FastThreadLocal.get()
        • 4.2 FastThreadLocal.set()

1.FastThreadLocal介绍

FastThreadLocal是Netty中常用的一个工具类, FastThreadLocal所使用的InternalThreadLocalMap内部不是采用哈希表, 而是直接通过数组索引的方式返回object, 省去了哈希表的查找过程, 因此效率相比于JDK的ThreadLocal更高。

2.FastThreadLocal分析

每个FastThreadLocal带有一个类型为int的index, 该属性在整个JVM中是全局唯一的, JVM中第一个实例化的FastThreadLocal的index为0, 第二个为1。

public FastThreadLocal() {
    index = InternalThreadLocalMap.nextVariableIndex();
}
 
public static int nextVariableIndex() {
    //nextIndex是一个静态变量,每次调用nextVariableIndex()都会自增1,让后赋给FastThreadLocal的index属性
    int index = nextIndex.getAndIncrement();
    if (index < 0) {
        nextIndex.decrementAndGet();
        throw new IllegalStateException("too many thread-local indexed variables");
    }
    return index;
}
 
static final AtomicInteger nextIndex = new AtomicInteger();

InternalThreadLocalMap可以通过FastThreadLocal的index值直接通过数据下标拿到相应的object。

public final V get(InternalThreadLocalMap threadLocalMap) {
    Object v = threadLocalMap.indexedVariable(index);
    if (v != InternalThreadLocalMap.UNSET) {
        return (V) v;
    }
 
    return initialize(threadLocalMap);
}
 
public Object indexedVariable(int index) {
    Object[] lookup = indexedVariables;
    return index < lookup.length? lookup[index] : UNSET;
}

如果要使用FastThreadLocal, 线程应该为FastThreadLocalThread, 内部使用InternalThreadLocalMap替换了JDK的ThreadLocalMap。

public static InternalThreadLocalMap get() {
    Thread thread = Thread.currentThread();
    if (thread instanceof FastThreadLocalThread) {
        //如果是FastThreadLocalThread,那么可以直接获取该FastThreadLocalThread的InternalThreadLocalMap
        return fastGet((FastThreadLocalThread) thread);
    } else {
        return slowGet();
    }
}
 
 
private static InternalThreadLocalMap slowGet() {
    //如果是普通的Thread,会先通过ThreadLocal找到Thread对应的InternalThreadLocalMap,该ThreadLocal是一个静态变量,在JVM中是唯一的
    ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
    InternalThreadLocalMap ret = slowThreadLocalMap.get();
    if (ret == null) {
        ret = new InternalThreadLocalMap();
        slowThreadLocalMap.set(ret);
    }
    return ret;
}

FastThreadLocal中有一个特殊的index

private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();

这个值在整个JVM中是唯一且不变的, 并且该值也是通过InternalThreadLocalMap.nextVariableIndex()来取值的, 意味着这个值永远是0。
正常的FastThreadLocal的index是从1开始的, 因为InternalThreadLocalMap中index为0的object是一个特殊的object。

private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
    Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
    Set<FastThreadLocal<?>> variablesToRemove;
    if (v == InternalThreadLocalMap.UNSET || v == null) {
        variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>());
        threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove);
    } else {
        variablesToRemove = (Set<FastThreadLocal<?>>) v;
    }
 
    variablesToRemove.add(variable);
}

每个FastThreadLocalThread的InternalThreadLocalMap中index为0的object是一个Set<FastThreadLocal<?>>, 这个set保存了FastThreadLocalThread所用到的所有的FastThreadLocal, 如果要删除FastThreadLocalThread中的所有Object, 直接删除set即可

public static void removeAll() {
    //获取FastThreadLocalThread的InternalThreadLocalMap
    InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
    if (threadLocalMap == null) {
        return;
    }
 
    try {
        //获取index为variablesToRemoveIndex的object,也就是上面提到的index为0的特殊的object,他是一个Set
        Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
        if (v != null && v != InternalThreadLocalMap.UNSET) {
            @SuppressWarnings("unchecked")
            //将object强转为Set
            Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v;
            //获取该FastThreadLocalThread的所有的FastThreadLocal
            FastThreadLocal<?>[] variablesToRemoveArray =
                    variablesToRemove.toArray(new FastThreadLocal[variablesToRemove.size()]);
            for (FastThreadLocal<?> tlv: variablesToRemoveArray) {
                //依次调用这些FastThreadLocal的remove方法
                tlv.remove(threadLocalMap);
            }
        }
    } finally {
        //最后将该FastThreadLocal的InternalThreadLocalMap置为null
        InternalThreadLocalMap.remove();
    }
}

FastThreadLocal.removeAll()方法会在DefaultThreadFactory中被调用, 通过DefaultThreadFactory这个工厂类new出来的Thread都是FastThreadLocalThread。

public void run() {
    try {
        r.run();
    } finally {
        FastThreadLocal.removeAll();
    }
}

每个线程在结束后都会调用FastThreadLocal.removeAll(), 这样该线程所有通过FastThreadLocal设置的Object在线程结束后都会被置为null, 避免了内存泄露。

3.FastThreadLocal结构分析

在这里插入图片描述

  1. InternalThreadLocalMap中并不是Entry的key-value结构, 而是Object数组
  2. 索引0位置存放FastThreadLocal的Set集合, 其他索引位置初始化为UNSET, 数据存入的时候更新为具体的Object
  3. FastThreadLocal中包含一个自增的index表示在InternalThreadLocalMap的数组中的索引位置
  4. Set<FastThreadLocal<?>>结构中存放FastThreadLocal的引用, 更容易解决内存泄漏的问题

4.FastThreadLocal方法分析

public class FastThreadLocalTest {
    private static FastThreadLocal<Object> threadLocal =
            new FastThreadLocal<Object>(){
                @Override
                protected Object initialValue() throws Exception {
                    return new Object();
                }
            };
    // 每个线程拿到的对象都是线程独享
    // 线程对对象的修改不会影响其他线程
    public static void main(String[] args) {
        new Thread(() -> {
            // 1.获取ThreadLocalMap
            // 2.直接通过索引取出对象
            // 3.初始化对象, 如果没有对象的话
            Object o = threadLocal.get();
            System.out.println(o);

            while (true){
                // 1.获取ThreadLocalMap
                // 2.直接通过索引set对象
                // 3.remove
                threadLocal.set(new Object());
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(() -> {
            Object o = threadLocal.get();
            System.out.println(o);

            while (true){
                System.out.println(threadLocal.get() == o);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
  • get方法
  • set方法

4.1 FastThreadLocal.get()

    public final V get() {
        InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
        Object v = threadLocalMap.indexedVariable(index);
        if (v != InternalThreadLocalMap.UNSET) {
            return (V) v;
        }

        return initialize(threadLocalMap);
    }

public static InternalThreadLocalMap get() {
    Thread thread = Thread.currentThread();
    if (thread instanceof FastThreadLocalThread) { // 当前线程是否为 FastThreadLocalThread 类型
        return fastGet((FastThreadLocalThread) thread);
    } else {
        return slowGet();
    }
}
private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
    InternalThreadLocalMap threadLocalMap = thread.threadLocalMap(); // 获取 FastThreadLocalThread 的 threadLocalMap 属性
    if (threadLocalMap == null) {
        thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
    }
    return threadLocalMap;
}
private static InternalThreadLocalMap slowGet() {
    ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap; 
    InternalThreadLocalMap ret = slowThreadLocalMap.get(); // 从 JDK 原生 ThreadLocal 中获取 InternalThreadLocalMap
    if (ret == null) {
        ret = new InternalThreadLocalMap();
        slowThreadLocalMap.set(ret);
    }
    return ret;
}
  1. 获取ThreadLocalMap

    • FastThreadLocalThread: fastGet() 方法获取 FastThreadLocalThread 的threadLocalMap 属性。
    • ThreadLocal: slowGet() 方法获取 InternalThreadLocalMap 就退化成 JDK 原生的 ThreadLocal 获取。
  2. 直接通过索引取出对象
    在这里插入图片描述

  3. 初始化对象, 如果没有对象的话
    在这里插入图片描述

4.2 FastThreadLocal.set()

public final void set(V value) {
    if (value != InternalThreadLocalMap.UNSET) { // 1\. value 是否为缺省值
        InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get(); // 2\. 获取当前线程的 InternalThreadLocalMap
        setKnownNotUnset(threadLocalMap, value); // 3\. 将 InternalThreadLocalMap 中数据替换为新的 value
    } else {
        remove();
    }
}
  1. 判断 value 是否为缺省值
  2. 获取当前线程的InternalThreadLocalMap
  3. 将 InternalThreadLocalMap 中对应数据替换为新的 value
  4. remove

setKnownNotUnset(): 将数据添加到 InternalThreadLocalMap

    private void setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {
        if (threadLocalMap.setIndexedVariable(index, value)) {
            addToVariablesToRemove(threadLocalMap, this);
        }
    }

    public boolean setIndexedVariable(int index, Object value) {
        Object[] lookup = indexedVariables;
        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 = index;
        newCapacity |= newCapacity >>>  1;
        newCapacity |= newCapacity >>>  2;
        newCapacity |= newCapacity >>>  4;
        newCapacity |= newCapacity >>>  8;
        newCapacity |= newCapacity >>> 16;
        newCapacity ++;

        Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
        Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
        newArray[index] = value;
        indexedVariables = newArray;
    }

private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
    Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex); // 获取数组下标为 0 的元素
    Set<FastThreadLocal<?>> variablesToRemove;
    if (v == InternalThreadLocalMap.UNSET || v == null) {
        variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>()); // 创建 FastThreadLocal 类型的 Set 集合
        threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove); // 将 Set 集合填充到数组下标 0 的位置
    } else {
        variablesToRemove = (Set<FastThreadLocal<?>>) v; // 如果不是 UNSET,Set 集合已存在,直接强转获得 Set 集合
    }
    variablesToRemove.add(variable); // 将 FastThreadLocal 添加到 Set 集合中
}
  1. 找到数组下标 index 位置

    1. 如果数组容量大于 FastThreadLocal 的 index 索引, 直接找到数组下标 index 位置将新 value 设置进去
    2. 在设置新的 value 之前, 将index 位置的元素取出, 如果旧元素还是UNSET缺省对象, 返回成功
  2. expandIndexedVariableTableAndSet(): 扩容

  3. addToVariablesToRemove(): 向 InternalThreadLocalMap 添加完数据之后, 将 FastThreadLocal 对象保存到待清理的 Set 中

remove():

public final void remove() {
    remove(InternalThreadLocalMap.getIfSet());
}
public static InternalThreadLocalMap getIfSet() {
    Thread thread = Thread.currentThread();
    if (thread instanceof FastThreadLocalThread) {
        return ((FastThreadLocalThread) thread).threadLocalMap();
    }
    return slowThreadLocalMap.get();
}
public final void remove(InternalThreadLocalMap threadLocalMap) {
    if (threadLocalMap == null) {
        return;
    }
    Object v = threadLocalMap.removeIndexedVariable(index); // 删除数组下标 index 位置对应的 value
    removeFromVariablesToRemove(threadLocalMap, this); // 从数组下标 0 的位置取出 Set 集合,并删除当前 FastThreadLocal
    if (v != InternalThreadLocalMap.UNSET) {
        try {
            onRemoval((V) v); // 空方法,用户可以继承实现
        } catch (Exception e) {
            PlatformDependent.throwException(e);
        }
    }
}
  1. 调用 InternalThreadLocalMap.getIfSet() 获取当前 InternalThreadLocalMap
    • 如果是 FastThreadLocalThread 类型, 直接取 FastThreadLocalThread 中 threadLocalMap 属性
    • 如果是普通Thread, 从 ThreadLocal 类型的 slowThreadLocalMap 中获取
  2. InternalThreadLocalMap 会从数组中定位到下标 index 位置的元素, 覆盖为缺省对象 UNSET
  3. 清理当前的 FastThreadLocal 对象, InternalThreadLocalMap 会取出数组下标 0 位置的 Set 集合, 删除当前FastThreadLocal

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/426160.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[架构之路-159]-《软考-系统分析师》-10-系统分析-6-现有业务流程分析, 系统分析最核心的任务

目录 第 10章 现有系统 分 析 1 0 . 6 现有业务流程分析 10.6.1 业务流程分析槪述 1 . 业务流程分析的步骤 2 . 业务流程分析的方法 10.6.2 业务-流程图TFD 1. T F D 的基本符号 2. TFD的绘制 10.6.3 业务 - 活动图 10.6.4 业务流程建模BPM 1. B P M 概述 2 . 标杆…

Pytest自动化测试框架一些常见的插件

Pytest拥有丰富的插件架构&#xff0c;超过800个以上的外部插件和活跃的社区&#xff0c;在PyPI项目中以“ pytest- *”为标识。 本篇将列举github标星超过两百的一些插件进行实战演示。 插件库地址&#xff1a;http://plugincompat.herokuapp.com/ 1、pytest-html&#xff1…

python机器学习决策树和SVM向量机算法实现红酒分类

1、红酒数据介绍 经典的红酒分类数据集是指UCI机器学习库中的Wine数据集。该数据集包含178个样本&#xff0c;每个样本有13个特征&#xff0c;可以用于分类任务。 具体每个字段的含义如下&#xff1a; alcohol&#xff1a;酒精含量百分比 malic_acid&#xff1a;苹果酸含量&a…

中科大ChatGPT学术镜像小白部署教程,全民都可以拥抱AI

docker…不会用…python不会用…服务器默认python版本3.6不会升级…代理也不会配置…各种命令不会用… 那么下面就是最简单办法&#xff0c;点点点即可【希望有帮助&#xff1f;】 文章目录一、体验镜像地址二、 基本配置2.1 config.py文件2.2 main.py文件三、下载项目四、项目…

FRP内网穿透配置

FRP内网穿透&#xff08;WIN&#xff09; 官方文档&#xff1a;点击进入 1.下载地址&#xff1a;点击进入 2.linux 压缩命令&#xff1a;tar -zxvf 包名&#xff0c;即可&#xff01; 3.linux配置服务端&#xff08;frps&#xff09; [common] bind_addr0.0.0.0 # frp监听的…

【NLP实战】基于Bert和双向LSTM的情感分类【下篇】

文章目录前言简介第一部分关于pytorch lightning保存模型的机制关于如何读取保存好的模型完善测试代码第二部分第一次训练出的模型的过拟合问题如何解决过拟合后记前言 本文涉及的代码全由博主自己完成&#xff0c;可以随意拿去做参考。如对代码有不懂的地方请联系博主。 博主…

TCP协议与UDP协议

1.TCP协议特点 1.1连接的建立与断开 TCP协议提供的是&#xff1a;面向连接、可靠的、字节流服务。使用TCP协议通信的双发必须先建立连接&#xff0c;然后才能开始数据的读写。双方都必须为该连接分配必要的内核资源&#xff0c;以管理连接的状态和连接上数据的传输。TCP连接是全…

从C语言到C++(第一章_C++入门_下篇)内联函数+auto关键字(C++11)+范围for +nullptr

目录 1. 内联函数 1.1 内联函数的概念 1.2 内联函数的特性 1.3 宏的优缺点和替代方法 2. auto关键字&#xff08;C11&#xff09; 2.1 改版前的auto 2.2 改版后的auto 2.3 auto 的使用场景 2.3.1处理很长的数据类型 2.3.2 auto 与指针结合起来使用&#xff1a; 2.4…

第2章 数据的类型

第2章 数据的类型 文章目录第2章 数据的类型2.2 为什么要进行区分2.3 结构化数据和非结构化数据案例&#xff1a;数据预处理字数/短语数特殊符号文本相对长度文本主题2.4 定量数据和定性数据2.4.1 案例&#xff1a;咖啡店数据2.4.2 案例&#xff1a;世界酒精消费量2.4.3 更深入…

4.18、TCP滑动窗口

4.18、TCP滑动窗口1.滑动窗口的介绍2.滑动窗口通信的例子1.滑动窗口的介绍 滑动窗口&#xff08;Sliding window&#xff09;是一种流量控制技术。早期的网络通信中&#xff0c;通信双方不会考虑网络的拥挤情况直接发送数据。由于大家不知道网络拥塞状况&#xff0c;同时发送数…

客户案例 | 迎接智能化浪潮,传统水厂数字化势在必行

关键发现 客户痛点&#xff1a;传统水厂业务离散&#xff0c;无法实现数据实时同步&#xff0c;为收集和分析处理数据并辅助决策带来障碍。需要智能化管理系统帮助水厂提升管理效率&#xff0c;优化管理流程&#xff0c;实现数字化、智能化的目标。 解决方案&#xff1a;天津腾…

你说你还不会Redis?别怕,今天带你搞定它!

Redis 前言 本文章是我学习过程中&#xff0c;不断总结而成&#xff0c;篇幅较长&#xff0c;可以根据选段阅读。 全篇17000字&#xff0c;图片 十三 张&#xff0c;预计用时1小时。 认识Redis 什么是Redis&#xff1f; 要使用一门技术&#xff0c;首先要知道这门技术是什…

pytorch进阶学习(三):在数据集数量不够时如何进行数据增强

对图片数据增强&#xff0c;可以对图片实现&#xff1a; 1. 尺寸放大缩小 2. 旋转&#xff08;任意角度&#xff0c;如45&#xff0c;90&#xff0c;180&#xff0c;270&#xff09; 3. 翻转&#xff08;水平翻转&#xff0c;垂直翻转&#xff09; 4. 明亮度改变&#xff08;变…

一零五五、mysql8.0高版本数据导入5.6低版本通解

背景 今日想将本机的mysql&#xff08;8.0&#xff09;中的数据库文件导出到远程服务器中的mysql&#xff08;5.6&#xff09;中&#xff0c;刚开始用source 一直报一大串ERROR&#xff0c;由于数据量比较大&#xff0c;那就直接用图形化工具导吧&#xff0c;连接上远程数据库&…

【常见CSS扫盲之渐变效果】好看的24种CSS渐变效果汇总(附源码)

【写在前面】web开发过程中&#xff0c;页面背景色想要一个渐变的效果很多时候网上一找全是官网那种很丑的色系&#xff0c;尤其是一些按钮和一些大背景色时候&#xff0c;不能搞得很yellow&#xff0c;今天我就做个工具人给大家罗列一些我在工作过程中总结的一些好看的渐变效果…

51单片机(8051系列)外部时钟

OUT&#xff08;输出引脚&#xff09;&#xff0c;IN&#xff08;输入引脚&#xff09;的区别 OUT(输出引脚) 输入引脚连接输入设备IN(输入引脚) 输出引脚连接输出设备外部时钟和内部时钟的区别 1、XTAL1和XTAL2引脚 内部时钟方式&#xff1a;必须在XTAL1和XTAL2引脚两端跨…

观早报 | 特斯拉储能超级工厂落沪;“华尔街之狼”募资550亿

今日要闻&#xff1a;京东拟今年发布千亿级产业大模型&#xff1b;特斯拉储能超级工厂落沪&#xff1b;“华尔街之狼”募资550亿&#xff1b;英特尔落户海南三亚&#xff1b;日本人要搞二次元老婆版 ChatGPT京东拟今年发布千亿级产业大模型 据《科创板日报》消息&#xff0c;京…

机器学习——L1范数充当正则项,让模型获得稀疏解,解决过拟合问题

问&#xff1a;使用L2范数正则项比L1范数正则项得到的是更为稀疏的解。 答&#xff1a;错误&#xff0c;L1范数正则项得到的是更稀疏的解。因为在L1正则项中&#xff0c;惩罚项是每个参数绝对值之和&#xff1b;而在L2正则项中&#xff0c;惩罚项是每个参数平方的和。L1正则项…

数字孪生智慧应急怎么实现?

根据国家对智慧应急建设的指示精神以及城市发展的迫切需求&#xff0c;智慧应急体系的建设应当从“大应急、大安全”的业务关切出发&#xff0c;聚焦“智慧应急”需求&#xff0c;融合对安全推进城市韧性建设的思考&#xff0c;将人工智能、物联网、大数据等高科技手段与应急救…

深度学习----DenseNet

1. 结构图 Input Shape : (3, 7, 7) — Output Shape : (2, 3, 3) — K : (3, 3) — P : (1, 1) — S : (2, 2) — D : (2, 2) — G : 1 The parts of this post will be divided according to the following arguments. These arguments can be found in the Pytorch documen…