Netty-FastThreadLocal vs ThreadLocal

在找netty分配内存时

ByteBufAllocator.DEFAULT.directBuffer(4);

最终进入到PooledByteBufAllocator.newDirectBuffer,首先就找threadCache分配,这就是一个FastThreadLocal

  protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        PoolThreadCache cache = threadCache.get();
        PoolArena<ByteBuffer> directArena = cache.directArena;
。。。
。。。

我们看看FastThreadLocal(简称ftl)和ThreadLocal(简称tl)区别在哪?为什么说ftl要配合ftlt(FastThreadLocalThread)使用性能才高于tl?

Threadlocal的具体解析可以参考threadlocal解析,这里大致说一下,threadLocal获取的值的时候先hash取模求出数组坐标i,然后获取entities[i],判断entities[i].key是不是自己,如果不是,就寻找i+1,循环。如果中间发现了entities[i].key==null,还要进行回收清理,将i到n (entities[n]==null,n是第一个为null的位置) 中间的元素重新hash,移动。效率一般,还存在内存泄漏。

我们看一下FastThreadLocal的实现。

  1.  

 

 

构造函数:每一个fastThreadLocal都带有一个唯一的下标。这个下标是静态的生成器产生的,所以能保证全局唯一。

    public FastThreadLocal() {
        index = InternalThreadLocalMap.nextVariableIndex();
    }

FastThreadLocal很多实现都是依靠InternalThreadLocalMap。InternalThreadLocalMap继承于UnpaddedInternalThreadLocalMap。UnpaddedInternalThreadLocalMap的主要属性。(Object[]数组直接装载用户的对象,不需要装载ftl的引用对象。因为根据ftl找对象是根据下标直接找,不会发生hash冲突,不需要再次比较key是否相等。)

// 这是一个ThreadLocal变量,用来兼容普通线程使用ftl
static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = new ThreadLocal<InternalThreadLocalMap>();
// 静态的坐标生成器
static final AtomicInteger nextIndex = new AtomicInteger();
// 数组 用来装实际对象的
Object[] indexedVariables;

FastThreadLocal的get方法。获取此线程的InternalThreadLocalMap然后再根据自身下标读取

    public final V get() {
        return get(InternalThreadLocalMap.get());
    }

    public final V get(InternalThreadLocalMap threadLocalMap) {
        Object v = threadLocalMap.indexedVariable(index);
        if (v != InternalThreadLocalMap.UNSET) {
            return (V) v;
        }

        return initialize(threadLocalMap);
    }

FastThreadLocal的set方法。先获取此线程的InternalThreadLocalMap ,根据下标设置值。

    // unsafe认为没有赋值
   public final void set(V value) {
        if (value != InternalThreadLocalMap.UNSET) {
            set(InternalThreadLocalMap.get(), value);
        } else {
            remove();
        }
    }

    public final void set(InternalThreadLocalMap threadLocalMap, V value) {
        if (value != InternalThreadLocalMap.UNSET) {
            if (threadLocalMap.setIndexedVariable(index, value)) {
                addToVariablesToRemove(threadLocalMap, this);
            }
        } else {
            remove(threadLocalMap);
        }
    }

了解一下addToVariablesToRemove方法。

    private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
        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);
    }

variablesToRemoveIndex是一个静态变量,等于0。再ftl的数组中,0号位置就是一个set,用来装着所有存入的ftl,协助removeAll。加入没有这个set,removeAll需要遍历整个数组,效率不高。有了这个set,只需要遍历整个set,根据每一个ftl的下标去清理数组就好了。

    private static final int variablesToRemoveIndex = 
InternalThreadLocalMap.nextVariableIndex();

InternalThreadLocalMap.get()。获取当前线程的InternalThreadLocalMap。ftl的get/set方法都需要先获取此线程的InternalThreadLocalMap。

    public static InternalThreadLocalMap get() {
        Thread thread = Thread.currentThread();
        if (thread instanceof FastThreadLocalThread) {
            return fastGet((FastThreadLocalThread) thread);
        } else {
            return slowGet();
        }
    }

获取当前线程的InternalThreadLocalMap分fast和slow,如果是ftlt就是fast获取。

看一下fast获取:ftlt本身内存有一个成员变量就是InternalThreadLocalMap。(初始化为32的长度数组)

    private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
        InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
        if (threadLocalMap == null) {
            thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
        }
        return threadLocalMap;
    }

如果是普通线程,slow获取 ,slowThreadLocalMap是一个tl变量,然后创建一个InternalThreadLocalMap赋值给slowThreadLocalMap。通过tl实现的

    private static InternalThreadLocalMap slowGet() {
        ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
        InternalThreadLocalMap ret = slowThreadLocalMap.get();
        if (ret == null) {
            ret = new InternalThreadLocalMap();
            slowThreadLocalMap.set(ret);
        }
        return ret;
    }

总结一下:

  • FastThreadLocal 通过分配下标直接定位 value ,不会有 hash 冲突,效率较高。
  • FastThreadLocal 采用空间换时间的方式来提高效率。
  • FastThreadLocal 需要配套 FastThreadLocalThread 使用,不然还不如原生 ThreadLocal。
  • FastThreadLocal 使用最好配套 FastThreadLocalRunnable,这样执行完任务后会主动调用 removeAll 来移除所有 FastThreadLocal ,防止内存泄漏。
  • FastThreadLocal 的使用也是推荐用完之后,主动调用 remove。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值