FastThreadLocal

- 每个 FastThread 包含一个 FastThreadLocalMap,每个 FastThreadLocalThread 中的多个 FastThreadLocal 占用不同的索引。
- 每个 InternalThreadLocalMap 的第一个元素保存了所有的 ThreadLocal 对象。之后的元素保存了每个 ThreadLocal 对应的 value
基本操作
get()
当前线程中
- 找到 ThreadLocal 的 index,value 加入 InternalThreadLocalMap 对应索引的位置
set()
当前线程中
- 找到 ThreadLocal 的 index,value 加入 InternalThreadLocalMap 对应索引的位置
- 将 ThreadLocal 加入当前线程 InternalThreadLocalMap 的第一个元素对应的集合
remove()
当前线程中
- 找到 ThreadLocal 的 index,InternalThreadLocalMap 中 index 对应的 value 置为 UNSET
- 将 ThreadLocal 从当前线程 InternalThreadLocalMap 的第一个元素集合中删除
关键设计点
兼容 ThreadLocal
当线程没有使用 FastThreadLocal 的时候,默认走 ThreadLocal 的逻辑。
初始大小
初始大小为 32
hash 算法
直接使用全局的自增,不存在Hash 冲突,以空间换时间
扩容条件是什么?如何扩容?
- 扩容条件:当前线程元素超出容量
- 扩容:元素数量扩展为向上取最近 2 次幂整数,比如,5 取8,31 取 64。
    public boolean setIndexedVariable(int index, Object value) {
        Object[] lookup = indexedVariables;
        // index 大于容量
        if (index < lookup.length) {
            Object oldValue = lookup[index];
            lookup[index] = value;
            return oldValue == UNSET;
        } else {
            // 扩容
            expandIndexedVariableTableAndSet(index, value);
            return true;
        }
    }
    
    private void expandIndexedVariableTableAndSet(int index, Object value) {
        Object[] oldArray = indexedVariables;
        final int oldCapacity = oldArray.length;
        int newCapacity;
        // 当小于 2的30次方时,容量扩展为向上取最近 2 次幂整数,比如,5 取8,31 取 64。
        if (index < ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD) {
            newCapacity = index;
            newCapacity |= newCapacity >>>  1;
            newCapacity |= newCapacity >>>  2;
            newCapacity |= newCapacity >>>  4;
            newCapacity |= newCapacity >>>  8;
            newCapacity |= newCapacity >>> 16;
            newCapacity ++;
        } else {
            newCapacity = ARRAY_LIST_CAPACITY_MAX_SIZE;
        }
        Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
        Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
        newArray[index] = value;
        indexedVariables = newArray;
    }
如何防止内存泄漏
自动: 使用ftlt执行一个被FastThreadLocalRunnable wrap的Runnable任务,在任务执行完毕后会自动进行ftl的清理。
final class FastThreadLocalRunnable implements Runnable {
    private final Runnable runnable;
    private FastThreadLocalRunnable(Runnable runnable) {
        this.runnable = ObjectUtil.checkNotNull(runnable, "runnable");
    }
    @Override
    public void run() {
        try {
            runnable.run();
        } finally {
            FastThreadLocal.removeAll();
        }
    }
    static Runnable wrap(Runnable runnable) {
        return runnable instanceof FastThreadLocalRunnable ? runnable : new FastThreadLocalRunnable(runnable);
    }
}
手动: ftl和InternalThreadLocalMap都提供了remove方法,在合适的时候用户可以(有的时候也是必须,例如普通线程的线程池使用ftl)手动进行调用,进行显示删除。
使用
final class FastThreadLocalRunnable implements Runnable {
    private final Runnable runnable;
    private FastThreadLocalRunnable(Runnable runnable) {
        this.runnable = (Runnable)ObjectUtil.checkNotNull(runnable, "runnable");
    }
    public void run() {
        try {
            this.runnable.run();
        } finally {
            // 如果用的是 FastThreadLocalRunnable ,默认会做清理
            FastThreadLocal.removeAll();
        }
    }
    static Runnable wrap(Runnable runnable) {
        return (Runnable)(runnable instanceof FastThreadLocalRunnable ? runnable : new FastThreadLocalRunnable(runnable));
    }
}
存在什么问题
1、空间浪费,所有线程的 ThreadLocalMap 数组大小是一样的
 比如,线程1 创建 100 个 ThreadLocal 对象。线程 1 里面有一个长度为 100 的数组。
 此时,第二个线程需要调用 ThreadLocal 100 的 get 方法,第二个线程需要分配 100 个 Object 对象。
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.concurrent.FastThreadLocalThread;
import java.util.ArrayList;
import java.util.List;
public class FastThreadLocalTest {
    private List<FastThreadLocal<String>> fastThreadLocals = new ArrayList<>();
    private List<ThreadLocal<String>> threadLocals = new ArrayList<>();
    void thread1Init() {
        new Thread (() -> {
            for (int i = 0; i < 31; i++) {
                ThreadLocal<String> threadLocal = new ThreadLocal<>();
                threadLocal.get();
                threadLocals.add(threadLocal);
            }
        }).start();
    }
    void thread2Init() {
        new Thread(() -> {
            threadLocals.get(threadLocals.size() - 1).get();
        });
    }
    void fastThread1Init() {
        new FastThreadLocalThread (() -> {
            for (int i = 0; i < 33; i++) {
                FastThreadLocal<String> fastThreadLocal = new FastThreadLocal<>();
                fastThreadLocal.get();
                fastThreadLocals.add(fastThreadLocal);
            }
        }).start();
    }
    void fastThread2Init() {
        new FastThreadLocalThread(() -> {
            fastThreadLocals.get(fastThreadLocals.size() - 1).get();
        });
    }
    public static void main(String[] args) {
        FastThreadLocalTest test = new FastThreadLocalTest();
        test.fastThread1Init();
        test.fastThread2Init();
        test.thread1Init();
        test.thread2Init();
    }
}
2、FastThreadLocal 需要配套 FastThreadLocalThread 使用,不然还不如原生 ThreadLocal。
 3、FastThreadLocal 使用最好配套 FastThreadLocalRunnable,这样执行完任务后会主动调用 removeAll 来移除所有
性能压测
netty 官方 mincrobench

















