本文基于Netty 4.1.39分析,作者才疏学浅,欢迎拍砖
FastThreadLocal 作用与JDK 原生的ThreadLocal功能是一样的,FastThreadLocal 持有指定类的对象,可以保证每个线程都持有一个唯一实例,每个线程持有实例都只在本线程内使用,所以不会有并发问题。但它的访问速度更快,顾名思义FastThreadLocal。
我们现在想使每个线程都持有一个MySQL数据库连接实例。假设MySQLClient 就是数据库连接类。如下代码所示:
但是只实例化FastThreadLocal是不够的,这样运行最终还是会回退到JDK中的ThreadLocal。FastThreadLocal必须配合FastThreadLocalThread使用。也就是说FastThreadLocal 必须运行在FastThreadLocalThread 线程中才会生效,否则回退到ThreadLocal模式。这样就生效了,如图:
FastThreadLocalThread 维护了一个InternalThreadLocalMap变量 。如图所示:
InternalThreadLocalMap 使用数组实现存储 FastThreadLocal对象和其持有的实例。数组的下标就是FastThreadLocal 的index值,对应元素就是其持有的实例。
FastThreadLocal 在初始化时,就生成了一个全局唯一递增的索引下标index。如果图所示:
对应上面的例子就是MYSQL_CLIENT_HOLDER 初始化时生成了index值 ,index从1开始,所以第一次实例FastThreadLocal时,index=1,则存储在InternalThreadLocalMap 数组的元素的状是 :
indexedVariables[1] = MySQLClient 实例
明白了FastThreadLocal ,FastThreadLocalThread,InternalThreadLocalMap的关系之后,MYSQL_CLIENT_HOLDER.get() 其内部实现的部署就很简单了。
1,获取当前线程
2,获取当前线程的InternalThreadLocalMap变量。
3,获取FastThreadLocal 实例MYSQL_CLIENT_HOLDER的index值。
4,获取InternalThreadLocalMap 数组 indexedVariables 下标为 index 的元素。
第一次获取实例时肯定是空的,所以会调用initialValue方法,此方法也是我们在new FastThreadLocal时覆盖的方法。如果我们不覆盖该方法,我们可以调用set方法,改方法会自动保存到当前线程的ThreadLocalMap变量中,这样下次再获取时就有实例了。
FastThreadLocal在get值时,是直接通过自身的index,从InternalThreadLocaMap的数组中直接取取出数据。而ThreadLocal每次get 都要先计算数组的下标,多了一个计算的步骤,其实就这么简单。
FastThreadLocal 必须配合FastThreadLocalThread使用,整个过程就是替换掉JDK ThreadLocalMap,实现无需每次都计算数组小标,从而更快。所有文字都可以简化一张图,如果所示