关于netty的FastThreadLocal的思考

Netty版本4.1.6。
与jdk的ThreadLocal相比,netty的FastThreadLoacl具有更高的读写性能,如何针对原生的ThreadLocal进行优化。
准备采用netty的FastThreadLoacl的线程都需要继承自netty实现的FastThreadLocalThread,在FastThreadLocalThread中,直接给出了如下字段。

private InternalThreadLocalMap threadLocalMap;

在这个前提下,当需要用到线程的FastThreadLocal的时候,相关操作都是在这个InternalThreadlocalMap中。
static final AtomicInteger nextIndex = new AtomicInteger(); /** Used by {@link FastThreadLocal} */ Object[] indexedVariables;

InternalThreadlocalMap主要由以上两个成员组成,其中indexedVariables作为一个Object[]数组,直接用来存放ThreadLocal对应的value,相比jdk的Entry[]数组相比,并没有存放ThreadLocal的key,那么是如何界定到ThreadLocal在数组中的具体位置呢。每个FastThreadLocal对象都会在相应的线程的ThreadLocalMap中被分配到对应的index,由以上的nextIndex分配,这样当需要根据相应的ThreadLocal取得相应的值时,就可以根据相应的下标,直接在数组上定位并得到。
从另一个角度上说,也避免了原本jdk上 hash的实现方式下,线性探测法导致的多次访问Entry数组的情况。
而大多数情况下,Object数组的第一个位置并不会存放具体的FastThreadLocal对应的值。
private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();

在FastThreadLocal类被加载的时候,将会得到ThreadLocalMap一个值作为variablesToRemoveIndex专门用来存放当前线程所有持有的ThreadLocal变量,当一个FastThreadLocal的值进入到Object[]中,将会把这个FastThreadLocal也放到Object[]variablesToRemoveIndex下标下的Set中,当一个线程运行在线程池中,只需要调用removeAll()方法就可以通过遍历该集合的方式完成当前残留ThreadLocal数据的清理,而不需要依次遍历到Object[]数组。
public static void removeAll() { InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet(); if (threadLocalMap == null) { return; }try { Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex); if (v != null && v != InternalThreadLocalMap.UNSET) { @SuppressWarnings("unchecked") Set> variablesToRemove = (Set>) v; FastThreadLocal[] variablesToRemoveArray = variablesToRemove.toArray(new FastThreadLocal[variablesToRemove.size()]); for (FastThreadLocal tlv: variablesToRemoveArray) { tlv.remove(threadLocalMap); } } } finally { InternalThreadLocalMap.remove(); } }

在removeAll()方法的支持下,Netty的默认线程工厂DefaultThreadFactory在产生线程时,将会默认在线程的run()方法的finally代码块中执行removeAll()。
private static final class DefaultRunnableDecorator implements Runnable {private final Runnable r; DefaultRunnableDecorator(Runnable r) { this.r = r; }@Override public void run() { try { r.run(); } finally { FastThreadLocal.removeAll(); } } }

除了上述的快速清理,最关键的地方在于也避免了内存泄漏和避免了jdk为了防止内存泄漏多出来启发式操作。由于在Set中,维持着对于各个FastThreadLocal中强引用,导致不会出现jdk中ThreadLocal被回收导致key不存在而产生的内存泄漏,保证removeAll()的调用会回收掉所有的FastThreadLocal,另一方面,也避免了原生的jdk的在存取过程中耗时的启发式内存泄漏检测和线性探测过程中冲突导致的泄漏处理。
【关于netty的FastThreadLocal的思考】

    推荐阅读