美文网首页
高性能Netty之FastThreadLocal源码解读

高性能Netty之FastThreadLocal源码解读

作者: 搬运工来架构 | 来源:发表于2018-09-22 16:31 被阅读33次

说到Netty的FastThreadLocal类,自然而然的想到JDK的ThreadLocal,为什么Netty觉得ThreadLocal不好?不够快吗?那么这里直接说下ThreadLocal存在的问题。

1、JDK ThreadLocal的问题

Thread中的ThreadLocalMap存储ThreadLocal,ThreadLocalMap内部使用ThreadLocalMap.Entry数组存储每一个ThreadLocal,存储计算和HashMap类似,要计算key的索引位置=key.threadLocalHashCode&(len-1),中间可能需要计算冲突,使用的是线程探测方法(当前索引在被占用下,使用下一个索引)。达到一定条件后,还需扩充数组长度,rehash,效率不是太高。另外,还需要使用者注意内存泄漏问题。

2、Netty FastThreadLocal 源码解读

这里主要从get,set 方法角度看代码,具体的实现细节可以跟代码查看。

 public FastThreadLocal() {        // 初始化当前线程对象下标索引index
        index = InternalThreadLocalMap.nextVariableIndex();        // 初始化当前线程对象下标清除标记索引index
        cleanerFlagIndex = InternalThreadLocalMap.nextVariableIndex();
    }    
    // 获得对象
    public final V get() {        // 获得当前线程对应的InternalThreadLocalMap,通过索引index从数组找到对应对象
        InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
        Object v = threadLocalMap.indexedVariable(index);        if (v != InternalThreadLocalMap.UNSET) {            // 
            return (V) v;
        }        // InternalThreadLocalMap获取不到对象,则通过初始化方法获取默认值
        V value = initialize(threadLocalMap);        // threadLocalMap注册到清理器,这里暂时不深究,猜测与垃圾回收或防止内存泄露有关
        registerCleaner(threadLocalMap); 
        return value;
    }    
    // 初始化
    private V initialize(InternalThreadLocalMap threadLocalMap) {
        V v = null;        try {
            v = initialValue();
        } catch (Exception e) {
            PlatformDependent.throwException(e);
        }        // 将当前对象索引index与对象进行绑定
        threadLocalMap.setIndexedVariable(index, v);
        addToVariablesToRemove(threadLocalMap, this);        return v;
    }    
    // 初始化值默认为null,子类可实现默认值
    protected V initialValue() throws Exception {        return null;
    }    
    // 设置对象
    public final void set(V value) {        if (value != InternalThreadLocalMap.UNSET) {
            InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();            if (setKnownNotUnset(threadLocalMap, value)) {
                registerCleaner(threadLocalMap);
            }
        } else {
            remove();
        }
    }    
    // 将对象设置进非默认对象对应的索引下标处
    private boolean setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {        if (threadLocalMap.setIndexedVariable(index, value)) {
            addToVariablesToRemove(threadLocalMap, this);            return true;
        }        return false;
    }    
    // InternalThreadLocalMap#setIndexedVariable,在对应的索引index数组上设置对象
    public boolean setIndexedVariable(int index, Object value) {
        Object[] lookup = indexedVariables;        if (index < lookup.length) {            // 查询OldValue,替换新对象,返回旧值是否为默认未创建的对象,也就是说对应的值是不是新对象
            Object oldValue = lookup[index];
            lookup[index] = value;            return oldValue == UNSET;
        } else {            // 扩容索引表,并且进行设值
            expandIndexedVariableTableAndSet(index, value);            return true;
        }
    }

总结:

1)FastThreadLocal初始化的时候,就会默认生成确定的索引下标,InternalThreadLocalMap存放索引表(数组),这样便于快速的找到对象。

2)针对remove方法,同样在类初始化的时候variablesToRemoveIndex会生成确定的索引下标,每次在进行set时都会将对应的FastThreadLocal存放到threadLocalMap中,这样方便在removeAll时进行清理对应的对象,防止内存泄露。

3)每次创建对象时,跟JDK ThreadLocal比都会相应的增加了很多索引下标,这在创建海量的FastThreadLocal对象时,数组占用的空间也不可小觑,所以这是Netty高性能之处:空间换时间的做法。

参考:

https://www.jianshu.com/p/17e6989d647a

https://www.jianshu.com/p/3fc2fbac4bb7

                                    扫描微信二维码↓↓↓,关注《**搬运工来架构**》
image

相关文章

网友评论

      本文标题:高性能Netty之FastThreadLocal源码解读

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