在http://blog.csdn.net/zero__007/article/details/78288448 中简单介绍了一下ThreadLocal,每个ThreadLocal实例都有一个唯一的threadLocalHashCode初始值,在ThreadLocalMap中设置或获取Entry时,会根据threadLocalHashCode&(len-1)的值去对应的槽中操作。
而ThreadLocal解决Hash 冲突使用线性探测的方法,当一个线程对应多个ThreadLocal实例的场景中,在命中的情况下基本上一次hash就可以找到位置,如果发生没有命中的情况,则会引发性能会急剧下降,本身是O(1),结果变成了O(n)当在读写操作频繁的场景,这点导致性能的后滞。
作为一个高并发框架,Netty对ThreadLocal作了一些优化,并提供一个性能更好的FastThreadLocal。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class FastThreadLocalThread extends Thread { private InternalThreadLocalMap threadLocalMap; public FastThreadLocalThread () { } public FastThreadLocalThread (Runnable target) { super (target); } public FastThreadLocalThread (ThreadGroup group, Runnable target) { super (group, target); } public FastThreadLocalThread (String name) { super (name); } public FastThreadLocalThread (ThreadGroup group, String name) { super (group, name); } public FastThreadLocalThread (Runnable target, String name) { super (target, name); } public FastThreadLocalThread (ThreadGroup group, Runnable target, String name) { super (group, target, name); } public FastThreadLocalThread (ThreadGroup group, Runnable target, String name, long stackSize) { super (group, target, name, stackSize); } public final InternalThreadLocalMap threadLocalMap () { return threadLocalMap; } public final void setThreadLocalMap (InternalThreadLocalMap threadLocalMap) { this .threadLocalMap = threadLocalMap; } }
Netty专门提供一个FastThreadLocalThread,继承了JDK的Thread,内部也有个InternalThreadLocalMap实例变量,并暴露了这个变量的getter/setter方法。
JDK中的ThreadLocalMap中的数组存放的是Entrty,key为ThreadLocal,value为真正保存的变量,而InternalThreadLocalMap中的数组里面的数据就是真正保存的变量值。
1 2 3 4 5 6 7 8 9 10 11 12 13 public class FastThreadLocal <V> { private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex(); private final int index; public FastThreadLocal () { index = InternalThreadLocalMap.nextVariableIndex(); } }
JDK的ThreadLocal中将threadLocalHashCode放在了ThreadLocal类中,然后再拿这个值通过计算去定位ThreadLocalMap中的槽,而Netty的FastThreadLocal直接直接保存index,该index是通过InternalThreadLocalMap的静态方法获取的,该值是一个递增的值,也就是说不同的线程,在同一个FastThreadLocal中保存的变量值,在其实例变量InternalThreadLocalMap中的槽都是一样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Test { static InternalThreadLocal<String> threadLocal = new InternalThreadLocal <String>(){ @Override protected String initialValue () throws Exception { return "zero" ; } }; public static void main (String[] args) { InternalThread thread0 = new InternalThread (new Runnable () { @Override public void run () { System.out.println(threadLocal.get()); } }); InternalThread thread1 = new InternalThread (new Runnable () { @Override public void run () { System.out.println(threadLocal.get()); } }); thread0.start(); thread1.start(); } }
上面的例子,线程私有的String变量,在其实例变量threadLocalMap里的数组,存放的位置下标都是1。由于InternalThreadLocal的index会递增,因此InternalThreadLocal实例再多的话也只会扩容,而不会发生Hash冲突。
在提一下FastThreadLocal中variablesToRemoveIndex,该变量是static final修饰的,会占用一个下标,即在InternalThreadLocalMap中会占一个槽。实际上该下标的元素是一个包装了IndentityHashMap的Set,每次FastThreadLocal中设置值的时候将自己加到该Set,移除值的时候将自己从该Set移除。也就是说初始值、设置值、删除值这几个功能的耗时比有值时的get更多。
性能比较看http://www.bubuko.com/infodetail-1932175.html