Netty关于ThreadLocal的优化

      在 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。
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);
    }

    /**
     * Returns the internal data structure that keeps the thread-local variables bound to this thread.
     * Note that this method is for internal use only, and thus is subject to change at any time.
     */
    public final InternalThreadLocalMap threadLocalMap() {
        return threadLocalMap;
    }

    /**
     * Sets the internal data structure that keeps the thread-local variables bound to this thread.
     * Note that this method is for internal use only, and thus is subject to change at any time.
     */
    public final void setThreadLocalMap(InternalThreadLocalMap threadLocalMap) {
        this.threadLocalMap = threadLocalMap;
    }
}
      Netty专门提供一个FastThreadLocalThread,继承了JDK的Thread,内部也有个InternalThreadLocalMap实例变量,并暴露了这个变量的getter/setter方法。

      JDK中的ThreadLocalMap中的数组存放的是Entrty,key为ThreadLocal,value为真正保存的变量,而InternalThreadLocalMap中的数组里面的数据就是真正保存的变量值。


public class FastThreadLocal {

    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中的槽都是一样的。
public class Test {
   static FastThreadLocal threadLocal = new FastThreadLocal(){
        @Override
        protected String initialValue() throws Exception {
            return "zero";
        }
    };

    public static void main(String[] args) {
        FastThreadLocalThread thread0 = new FastThreadLocalThread(new Runnable() {
            @Override
            public void run() {
                System.out.println(threadLocal.get());
            }
        });
        FastThreadLocalThread thread1 = new FastThreadLocalThread(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修饰的,其值就是0,在InternalThreadLocalMap中会占index=0的槽。实际上该下标的元素是一个包装了IndentityHashMap的Set,每次FastThreadLocal中设置值的时候将自己加到该Set,移除值的时候将自己(FastThreadLocal实例)从该Set移除。也就是说初始值、设置值、删除值这几个功能的耗时比有值时的get更多。
      关于ThreadLocal与FastThreadLocal的性能对比数据,可以看下http://www.bubuko.com/infodetail-1932175.html

你可能感兴趣的:(#,【ThreadLocal】)