前言ThreadLocal是通过ThreadLocalMap中入口的key弱引用的。如果出现GC情况,没有被其他对象引用,会被回收,但是ThreadLocal对应的值却不会被回收,容易造成内存泄漏,间接导致内存溢出,假数据丢失;那么问题来了,有没有更高效的ThreadLocal;今天我们来分析一波FastThreadLocalThread1、FastThreadLocalThread源码分析Netty为提高某些场景下的性能,改进jdkThreadLocal,Netty实现的FastThreadLocal优化了Java原生ThreadLocal的访问速度和存储速度。避免了检测弱引用导致的值回收困难,以及数组位置冲突导致的线性搜索问题。解决这些问题不是免费的;Netty实现的FastThreadLocal底层也是通过数组来存储值对象,并且使用自身与Java原生的ThreadLocal不同的是Entry的key,FastThreadLocal通过保存数组的全局唯一下标来实现对值的快速访问。同时FastThreadLocal还实现了清理对象的方法;1、FastThreadLocalThread在Netty中。使用FastThreadLocal实现线程局部变量,需要将线程封装为FastThreadLocalThread。如果不是FastThreadLocalThread,则会使用slowThreadLocalMap的ThreadLocal来存储变量副本;io.netty.util.concurrent.DefaultThreadFactory@OverridepublicThreadnewThread(Runnabler){Threadt=newThread(FastThreadLocalRunnable.wrap(r),prefix+nextId.incrementAndGet());//generaldaemon为false,表示没有设置为adaemonthreadif(t.isDaemon()!=daemon){t.setDaemon(daemon);}//默认优先级为5if(t.getPriority()!=priority){t.setPriority(priority);}returnt;}protectedThreadnewThread(Runnabler,Stringname){returnnewFastThreadLocalThread(threadGroup,r,name);}FastThreadLocalThread继承自Thread类,有如下成员变量:io.netty.util.concurrent.FastThreadLocalThread//任务执行后,是否清除FastThreadLocal标记privatefinalbooleancleanupFastThreadLocals;//类似于Thread类ThreadLocalMap,为了实现FastThreadLocalprivateInternalThreadLocalMapthreadLocalMap;2.InternalThreadLocalMapFastThreadLocalThread.threadLocalMap是InternalThreadLocalMap对象的一个??实例。第一次获取FTL数据时,会初始化FastThreadLocalThread.threadLocalMap,调用的构造函数如下:privateInternalThreadLocalMap(){//为简单起见,InternalThreadLocalMap的父类//UnpaddedInternalThreadLocalMap不展开引入super(newIndexedVariableTable());}//默认数组大小为32,用UNSET对象填充数组//如果下标处的数据为UNSET,则表示没有数据privatestaticObject[]newIndexedVariableTable(){Object[]array=newObject[32];Arrays.fill(array,UNSET);returnarray;}写入时为了避免并发访问其他数据影响同一个cpubufferline,使用了cacheline填充技术(cpubufferlinefilling),并在类定义中声明如下长字段用于填充;//InternalThreadLocalMap//Cachelinepadding(mustbepublic)//WithCompressedOopsenabled,aninstanceofthisclassshoulddoccupyatleast128bytes.publiclongrp1,rp2,rp3,rp4,rp5,rp6,rp7,rp8,rp9;FTL使用的数组下标由InternalThreadLocalMap=newAtomegerneger=static()中的静态变量nextIndexIndexIndexIndexIndexIndexIndex统一递增生成;publicstaticintnextVariableIndex(){//Netty中所有的FTL数组下标都是通过自增这个静态变量实现的//使用静态变量生成数组中所有FTL元素的下标会出现问题,//会导致InternalThreadLocalMap不必要的数组自动扩容intindex=nextIndex.getAndIncrement();if(index<0){nextIndex.decrementAndGet();thrownewIllegalStateException("toomanythread-localindexedvariables");}returnindex;}InternalThreadLocalMap.nextVariableIndex()方法获取FastThreadLocalThread.threadLocalMap数组中FTL的下标,因为InternalThreadLocalMap.nextVariableIndex()使用静态字段nextIndex增量维护所有的FTL下标,会导致后面实例化的FTL下标过大。如果FTL下标大于对应的FastThreadLocalThread.threadLocalMap数组的长度,则数组会自动扩容,如下:下面复杂的实现是将newCapacity归一化到最接近的index2,//这段代码在早期的jdkHashMap中已经看到intnewCapacity=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;}3.FastThreadLocal构造函数:有两个重要的下标字段,FTL不仅在FastThreadLoc中alThread.threadLocalMap保存了用户实际使用的值(数组中下标为index),数组中也保存了清除记录的相关数据,即下标变量ToRemoveIndex。一般来说,variablesToRemoveIndex=0;因为variablesToRemoveIndex是Staticvariable,所以全局唯一;//如果数据放在FTL中,实际上会调用它的set或get函数,在//FastThreadLocalThread.threadLocalMap数组的//variablesToRemoveIndex下标放置一个IdentityHashMap,//并把这个FTL放到IdentityHashMap中,在后续清理的时候取出//variablesToRemoveIndex下标处的IdentityHashMap进行清理(){index=InternalThreadLocalMap.nextVariableIndex();}用户扩展函数://初始化值functionprotectedVinitialValue()throwsException{returnnull;}//让用户有机会在FTL被移除的时候做一些操作protectedvoidonRemoval(@SuppressWarnings("UnusedParameters")Vvalue)throwsException{}4.1最新版本不再使用FastThreadLocalThreadcleanupFastThreadLocals字段/***true,表示线程结束时会主动清理FTL。参见FastThreadLocalRunnable类*false,required将FTL放入后台清理线程的队列中*///如果我们有一个move来包裹Runnable,这个会被设置为true//这个字段用来标识线程是否会主动结束时清理FTL。设置时创建privateInternalThreadLocalMapthreadLocalMap;publicFastThreadLocalThread(Runnabletarget){super(FastThreadLocalRunnable.wrap(target));cleanupFastThreadLocals=true;}4.set方法publicfinalvoidset(Vvalue){//判断设置值是否为默认值if(value!=InternalThreadLocalMap.UNSET){//获取当前线程的InternalThreadLocalMap,如果当前线程是FastThreadLocalThread,则直接通过threadLocalMap引用获取//否则通过jdk的nativethreadLocal获取InternalThreadLocalMapthreadLocalMap=InternalThreadLocalMap.get();//在FastThreadLocal对应的index下替换具有新值的目标值setKnownNotUnset(threadLocalMap,value);}else{//如果放置的对象是UNSET,则表示清理,FTL会被清理掉,类似毒丸对象remove();}}这里扩展方会调用InternalThreadLocalMap.expandIndexedVariableTableAndSetprivatevoidsetKnownNotUnset(InternalThreadLocalMapthreadLocalMap,Vvalue){//将实际对象放在数组下标索引处,如果索引大于数组长度,会进行数组扩容。if(threadLocalMap.setIndexedVariable(index,value)){//放置成功后,将FTL添加到variablesToRemoveIndex下标的//IdentityHashMap中,等待后续清理addToVariablesToRemove(threadLocalMap,this);}}/***将FTL添加到下标为variablesToRemoveIndex的IdentityHashMap*IdentityHashMap的特性可以保证同一个实例不会被多次添加到这个位置*/@SuppressWarnings("unchecked")privatestaticvoidaddToVariablesToRemove(InternalThreadLocalMapthreadLocalMap,FastThreadLocal>variable){//获取变量ToRemoveIndex标识HashMapObjectv=threadLocalMap.indexedVariable(variablesToRemoveIndex);Set
