导航菜单
路很长,又很短
博主信息
昵   称:Cocodroid ->关于我
Q     Q:2531075716
博文数:297
阅读量:606212
访问量:56576
至今:
×
云标签 标签球>>
云标签 - Su的技术博客
博文->>首页 博主的更多博文>>
高性能Netty之FastThreadLocal源码解读
Tags : 高性能,Netty,FastThreadLocal,源码发表时间: 2018-09-09 13:16:36
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。
比如: 转自:Su的技术博客  原文地址:


说到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

打赏
打赏
关注公众号
公众号
类别:源码| 阅读(270)| 赞 (0)
评论
暂无评论!
发表评论
昵  称:

验证码:

内  容:

    同时赞一个 赞