资讯专栏INFORMATION COLUMN

【源起Netty 外传】FastThreadLocal怎么Fast?

gxyz / 410人阅读

摘要:实现原理浅谈帮助理解的示意图中有一属性,类型是的静态内部类。刚刚说过,是一个中的静态内部类,则是的内部节点。这个会在线程中,作为其属性初始是一个数组的索引,达成与类似的效果。的方法被调用时,会根据记录的槽位信息进行大扫除。

概述

FastThreadLocal的类名本身就充满了对ThreadLocal的挑衅,“快男”FastThreadLocal是怎么快的?源码中类注释坦白如下:

/**
 * ...
 * Internally, a {@link FastThreadLocal} uses a constant index in an array, instead of using hash code and hash table,
 * to look for a variable.  Although seemingly very subtle, it yields slight performance advantage over using a hash
 * table, and it is useful when accessed frequently.
 * ...
 */

大概意思就是:用索引代替了ThreadLocal中的threadLocalHashCode,当请求频繁时,这个小改动就会显现其效果。
提到FastThreadLocal,就不得不提它的好基友FastThreadLocalThread,简单来说,FastThreadLocal就是为FastThreadLocalThread量身打造的!

FastThreadLocalThread又是哪个单位的?且听我慢慢道来……

FastThreadLocalThread

一般来说,Netty的client端,是这么创建的:

EventLoopGroup group = new NioEventLoopGroup();

沿着调用链,层层深入NioEventLoopGroup的构造函数,在MultithreadEventExecutorGroup构造块,会看到这样的逻辑:

if (executor == null) {
    executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}

newDefaultThreadFactory()方法,功能如其名,创建了DefaultThreadFactory工厂:

protected ThreadFactory newDefaultThreadFactory() {
    return new DefaultThreadFactory(getClass());
}

DefaultThreadFactory工厂的产品,就是FastThreadLocalThread

ok,FastThreadLocalThread物种起源的事儿暂且放下,我们来简单过一下原版ThreadLocal的工作机制。

ThreadLocal

ThreadLocal大家应该并不陌生,两个最核心API

//赋值方法
public void set(T value);

//取值方法
public T get();

用以并发环境下,线程生命周期内的存取数据操作,隔离其它线程干扰。

实现原理浅谈

帮助理解的示意图:

Thread中有一属性threadLocalsThreadLocal.ThreadLocalMap类型(ThreadLocalMap是ThreadLocal的静态内部类)。ThreadLocal的set(T value)方法被调用时,会将参数value存放于当前线程的threadLocals中,想要获取时,再从threadLocals中获取。

刚刚说过,threadLocals是一个ThreadLocalMap(ThreadLocal中的静态内部类),Entry则是ThreadLocalMap的内部节点。而ThreadLocalMap作为一个Map,人设是这样的ThreadLocalMap>,T>,即key=ThreadLocalvalue=T

存入 set(T value)

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);    //根据当前线程获取ThreadLocalMap
    if (map != null)
        map.set(this, value);    //map的key直接使用的当前ThreadLocal对象
    else
        createMap(t, value);
}  

线程向ThreadLocal存入时,第一次调用将在线程中分配一块空间,初始大小为Entry[16]数组。然后,以作为key的ThreadLocal计算出hashCode,稍加计算得出Entry[16]数组的索引i。最后,为槽位Entry[i]赋值上value。

获取 T get()

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);    //找到与当前线程绑定的ThreadLocalMap
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

获取时,先找到与当前线程绑定的ThreadLocalMap,然后你懂得……

听上去一副满满的HashMap的套路,其实内在有很大差别,比如ThreadLocal的Entry实现了WeakReference弱引用)。

一个小问题

ThreadLocal为什么用Entry数组作为内置实现?而且初始就16位,还有扩容等实现。对某一线程而言,ThreadLocal不是只能存储一个值吗?这么想的话,用单个Object就能存储了……

这个万恶的问题,当时困扰了我很长时间,其实想通后很简单:
Entry数组为了实现ThreadLocalMap,而ThreadLocalMap君的key是ThreadLocal,多个ThreadLocal都可以存储在ThreadLocalMap中。没错,A线程只用一个ThreadLocal的话,确实用一个Object作为内置实现就搞定了,但是A线程使用多个ThreadLocal时,无法满足!

FastThreadLocal

ThreadLocal就分析到这里,让我们回归FastThreadLocal。

结构及关键方法示意图:

前文提到过,FastThreadLocal的关键是indexindex可以理解成一个FastThreadLocal对象的唯一ID。这个index会在FastThreadLocalThread线程中,作为其indexedVariables属性(初始是一个Object[32]数组)的索引,达成与ThreadLocal类似的效果。

值得一提的是Object[] indexedVariables的第0位(Object[0]),用于存放未来会被释放的数据,其实存储的就是之前FastThreadLocal的set方法操作过的槽位信息。FastThreadLocal的remove方法被调用时,会根据记录的槽位信息进行大扫除。

FastThreadLocal相关内容就聊到这里,更多细节请翻看源码!

感谢

ThreadLocal源码解读

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68317.html

相关文章

  • 源起Netty 前传】Linux网络模型小记

    摘要:非阻塞模型这种也很好理解,由阻塞的死等系统响应进化成多次调用查看数据就绪状态。复用模型,以及它的增强版就属于该种模型。此时用户进程阻塞在事件上,数据就绪系统予以通知。信号驱动模型应用进程建立信号处理程序时,是非阻塞的。 引言 之前的两篇文章 FastThreadLocal怎么Fast?、ScheduledThreadPoolExecutor源码解读 搞的我心力交瘁,且读源码过程中深感功...

    Null 评论0 收藏0
  • 源起Netty 外传】ServiceLoader详解

    摘要:答曰摸索直译为服务加载器,最终目的是获取的实现类。代码走起首先,要有一个接口形状接口介绍然后,要有该接口的实现类。期具体实现依靠的内部类,感性趣的朋友可以自己看一下。总结重点在于可跨越包获取,这一点笔者通过多模块项目亲测延时加载特性 前戏 netty源码注释有云: ... If a provider class has been installed in a jar file tha...

    MoAir 评论0 收藏0
  • 源起Netty 外传】System.getPropert()详解

    摘要:阅读源码时,发现很多,理所当然会想翻阅资料后,该技能,姿势如下环境中的全部属性全部属性注意如果将本行代码放在自定义属性之后,会不会打出把自定义属性也给获取到可以结论会获取目前环境中全部的属性值,无论系统提供还是个人定义系统提供属性代码中定义 阅读源码时,发现很多System.getProperty(xxx),理所当然会想:whats fucking this? 翻阅资料后,Get该技能...

    lixiang 评论0 收藏0
  • 源起Netty 外传】ScheduledThreadPoolExecutor源码解读

    引言 本文是源起netty专栏的第4篇文章,很明显前3篇文章已经在偏离主题的道路上越来越远。于是乎,我决定:继续保持…… 使用 首先看看源码类注释中的示例(未改变官方示例逻辑,只是增加了print输出和注释) import java.time.LocalTime; import java.util.concurrent.Executors; import java.util.concurrent....

    funnyZhang 评论0 收藏0
  • 源起Netty 外传】ScheduledThreadPoolExecutor源码解读

    引言 本文是源起netty专栏的第4篇文章,很明显前3篇文章已经在偏离主题的道路上越来越远。于是乎,我决定:继续保持…… 使用 首先看看源码类注释中的示例(未改变官方示例逻辑,只是增加了print输出和注释) import java.time.LocalTime; import java.util.concurrent.Executors; import java.util.concurrent....

    Eastboat 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<