美文网首页
FastThreadLocal总结

FastThreadLocal总结

作者: 圣村的希望 | 来源:发表于2019-10-06 12:43 被阅读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();
    }
}

使用关键点如下:

  • new FastThreadLocal实例,重写initialValue方法,在调用其get方法获取不到对象时就会调用initialValue方法。
  • new FastTHreadLocalThread实例,调用get获取当前线程的本地变量

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;
  • 首先获取当前线程的InternalThreadLocalMap对象,FastThreadLocalThread线程中有个threadLocalMap属性,普通线程就从slowThreadLocalMap属性中的ThreadLocal中获取InternalThreadLocalMap对象。
  • 根据FastThreadLocal对象的index属性,从InternalThreadLocalMap中获取线程本地变量。在创建FastThreadLocal实例的时候,就生成了该对象实例的index属性,保证了在一个JVM实例中的唯一性。在InternalThreadLocalMap中是有个一个Object[]数组,然后通过index从数组中获取当前线程保存的本地变量。

  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);
    }
  • 判断value是否为UNSET对象,是则删除当前FastThreadLocal对象。否则获取当前线程的InternalThreadLocalMap对象,设置当前线程本地变量
  • 设置当前线程本地变量,初始indexedVariable数组是32位,长度不够就进行扩容,然后把当前线程的其他FastThreadLocal对象转移过去,因为一个线程可能有多个线程本地变量,也即多个FastThreadLocal对象。
  • 在进行了indexedVariable数组扩容或者替换了原有UNSET对象时,会返回true,就会将当前的FastThreadLocal对象添加到待删除集合中,这个是放置在indexedVariable数组的索引下标为0的位置。他是个Set<FastThreadLocal<?>> 集合,存放的是当前线程的所有线程本地变量FastThreadLocal对象实例。

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

相关文章

网友评论

      本文标题:FastThreadLocal总结

      本文链接:https://www.haomeiwen.com/subject/hmoepctx.html