FastThreadLocal总结

2019-10-06  本文已影响0人  圣村的希望

    FastThreadLocal是netty中存放线程本地变量的工具类,是对JDK中的ThreadLocal的改良,在netty中使用FastThreadLocal替代ThreadLocal的使用。

FastThreadLocal使用示例:

public class TestFastThreadLocal {

    public static FastThreadLocal<Object> fastThreadLocal = new FastThreadLocal<Object>() {
        @Override
        protected Object initialValue() throws Exception {
            return new Object();
        }
    };

    public static void main(String[] args) {
        new FastThreadLocalThread(() -> {
            Object object = fastThreadLocal.get();
            System.out.println(object);
        }).start();

        new FastThreadLocalThread(() -> {
            Object object = fastThreadLocal.get();
            System.out.println(object);
        }).start();
    }
}

使用关键点如下:

FastThreadLocal源码分析:

  1. 首先看下其中的重要属性和方法

    FastThreadLocal类图.png
    在这里我们主要关注下其中的index属性get()方法set()方法
  2. index属性是用来标志当前FastThreadLocal为JVM全局唯一,也是保证FastThreadLocal高性能的基础。

    private final int index;

    //创建FastThreadLocal实例的时候会设置其index属性
    public FastThreadLocal() {
        index = InternalThreadLocalMap.nextVariableIndex();
    }

    public static int nextVariableIndex() {
        int index = nextIndex.getAndIncrement();
        if (index < 0) {
            nextIndex.decrementAndGet();
            throw new IllegalStateException("too many thread-local indexed variables");
        }
        return index;
    }

    static final AtomicInteger nextIndex = new AtomicInteger();

在创建FastThreadLocal实例的时候会设置iindex属性,其通过一个Integer原子类属性nextIndex的自增来获取,保证FastThreadLocal在一个JVM实例中的唯一。

  1. FastThreadLocal.get()方法,通过其get方法可以获取当前线程保存的本地变量。
    //获取当前线程的本地变量
    public final V get() {
        //获取当前线程的InternalThreadLocalMap
        InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
        //通过当前线程的ThreadLocalMap的indexedVariable的index属性来获取当前线程本地变量
        Object v = threadLocalMap.indexedVariable(index);
        //如果对象不是默认的unset的返回
        if (v != InternalThreadLocalMap.UNSET) {
            return (V) v;
        }
        //如果返回对象为默认的UNSET的Object对象,调用自定义的initialize方法创建对象
        return initialize(threadLocalMap);
    }

    //获取当前线程的InternalThreadLocalMap
    public static InternalThreadLocalMap get() {
        //获取当前线程
        Thread thread = Thread.currentThread();
        //判断当前线程是否为FastThreadLocalThread,是则用采用FastThreadLocal中获取线程本地变量
        if (thread instanceof FastThreadLocalThread) {
            return fastGet((FastThreadLocalThread) thread);
        } else {
            //否则从ThreadLocal中获取当前线程本地变量
            return slowGet();
        }
    }

    //从FastThreadLocalThread中获取当前线程的InternalThreadLocalMap对象
    private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
        InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
        if (threadLocalMap == null) {
            thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
        }
        return threadLocalMap;
    }

    //从ThreadLocal中获取当前线程的InternalThreadLocalMap对象
    private static InternalThreadLocalMap slowGet() {
        ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
        InternalThreadLocalMap ret = slowThreadLocalMap.get();
        if (ret == null) {
            ret = new InternalThreadLocalMap();
            slowThreadLocalMap.set(ret);
        }
        return ret;
    }

    //在UnpaddedInternalThreadLocalMap中有个ThreadLocal包装的静态变量
    static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = new ThreadLocal<InternalThreadLocalMap>();

    //通过FastThreadLocal的index来获取当前线程的本地变量
    public Object indexedVariable(int index) {
        Object[] lookup = indexedVariables;
        return index < lookup.length? lookup[index] : UNSET;
    }

    //存放Object的数组,通过index属性从数组中获取对应索引下的对象
    Object[] indexedVariables;

  FastThreadLocal较ThreadLocal高效的原因:在FastThreadLocalThread线程中有个InternalThreadLocalMap类型的属性threadLocalMap,InternalThreadLocalMap里面有个indexedVariables属性是通过Object[]数组来保存线程本地变量信息。FastThreadLocal通过index属性值来定位Object[]数组中的元素。但是ThreadLocal的原理是每次拿到key的hashCode,然后和ThreadLocalMap中的Entry数组长度进行取余,然后再放到ThreadLocalMap中。

  1. FastThreadLocal.set()方法,set方法看起来简单,其实做的事情也不少
    public final void set(V value) {
        //当前value对象不是UNSET对象,获取当前线程的InternalThreadLocalMap对象,设置value
        if (value != InternalThreadLocalMap.UNSET) {
            InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
            setKnownNotUnset(threadLocalMap, value);
        } else {
            remove();
        }
    }

    //设置当前线程FastThreadLocal变量,并且添加到remove set中
    private void setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {
        if (threadLocalMap.setIndexedVariable(index, value)) {
            addToVariablesToRemove(threadLocalMap, this);
        }
    }

    public boolean setIndexedVariable(int index, Object value) {
        Object[] lookup = indexedVariables;
        if (index < lookup.length) {
            Object oldValue = lookup[index];
            lookup[index] = value;
            return oldValue == UNSET;
        } else {
            expandIndexedVariableTableAndSet(index, value);
            return true;
        }
    }

    private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
        //获取InternalThreadLocalMap中的indexedVariable数组的下标为0的对象(待删除的FastThreadLocal set对象)
        Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
        Set<FastThreadLocal<?>> variablesToRemove;
        if (v == InternalThreadLocalMap.UNSET || v == null) {
            variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>());
            threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove);
        } else {
            variablesToRemove = (Set<FastThreadLocal<?>>) v;
        }

        variablesToRemove.add(variable);
    }

为什么这里要使用Set来保存当前线程的所有本地变量?
    因为使用set来保存,是为了方便删除线程的本地变量,在removeAll的时候,把set转换成数组,便利数组就可以方便删除当前线程的所有本地变量,不需要再进行遍历indexedVariable数组,在本地线程变量比较多的时候,index索引会很散,处理起来效率不高。

上一篇 下一篇

猜你喜欢

热点阅读