转自:https://blog.csdn.net/youaremoon/article/details/50345651
并发的一个很大的朋友就是锁,因为锁可以在并发的情况下保护一些公共的变量;并发的一个很大的敌人也是锁,因为锁带来了很大的性能开销。
很多时候我们必须加锁来应对并发带来的线程安全问题,而ThreadLocal则给我们开辟了另外一个思路,将数据保存在线程的私有字段中,使一个线程无法读到其他线程的数据, 这样的数据存储方式,自然就线程安全。 那么ThreadLocal本身还有没有可以挖掘的优化点呢,netty告诉你,有!
我们知道ThreadLocal的实现是在Thread里放一个类似map(虽然叫map,但实际上是纯数组实现)的数据结构来存数据,而这个自定义的map要查找一个元素主要分成两步:1、通过元素的hashcode计算得到对应的index,判断对应index上是否有数据,如果有数据且key==yourThreadLocal则匹配成功;2、否则按顺序依次挪到下一个位置进行对比,直到找到yourThreadLocal。 这样查询一个元素可能需要很多步。同样插入或移除一个值,操作步数也比较多。
下面来看看netty君是怎么做的,首先基本思路和jdk的ThreadLocal相同,在一个线程上增加一个私有字段存数据。然而原有的ThreadLocal并没有提供定制的接口,咋办呢?netty继承Thread实现了一个FastThreadLocalThread,该FastThreadLocalThread实现了FastThreadLocalAccess接口:
- public interface FastThreadLocalAccess {
- InternalThreadLocalMap threadLocalMap();
- void setThreadLocalMap(InternalThreadLocalMap threadLocalMap);
- }
- private InternalThreadLocalMap() {
- super(newIndexedVariableTable());
- }
- private static Object[] newIndexedVariableTable() {
- Object[] array = new Object[32];
- Arrays.fill(array, UNSET);
- return array;
- }
InternalThreadLocalMap根据netty的实际情况还加入了一些其他的字段做一些定制化的的数据缓存,这样在ThreadLocal的基础上功能又增强了。不过话说回来ThreadLocal毕竟是公共的解决方案,不太可能放这些乱七八糟的东西。来看看这些netty引入的字段:
- static ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap;
- static final AtomicInteger nextIndex = new AtomicInteger();
- /** Used by {@link FastThreadLocal} */
- Object[] indexedVariables;
- // Core thread-locals
- int futureListenerStackDepth;
- int localChannelReaderStackDepth;
- Map<Class<?>, Boolean> handlerSharableCache;
- IntegerHolder counterHashCode;
- ThreadLocalRandom random;
- Map<Class<?>, TypeParameterMatcher> typeParameterMatcherGetCache;
- Map<Class<?>, Map<String, TypeParameterMatcher>> typeParameterMatcherFindCache;
- // String-related thread-locals
- StringBuilder stringBuilder;
- Map<Charset, CharsetEncoder> charsetEncoderCache;
- Map<Charset, CharsetDecoder> charsetDecoderCache;
- // 直接根据index获取
- public Object indexedVariable(int index) {
- Object[] lookup = indexedVariables;
- return index < lookup.length? lookup[index] : UNSET;
- }
- /**
- * @return {@code true} if and only if a new thread-local variable has been created
- */
- 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 {
- // 如果要查询的index不在indexedVariables范围,则需要先扩展在设置
- expandIndexedVariableTableAndSet(index, value);
- return true;
- }
- }
- private void expandIndexedVariableTableAndSet(int index, Object value) {
- Object[] oldArray = indexedVariables;
- final int oldCapacity = oldArray.length;
- // newCapacity-> 32,64,128,256,512....
- int newCapacity = index;
- newCapacity |= newCapacity >>> 1;
- newCapacity |= newCapacity >>> 2;
- newCapacity |= newCapacity >>> 4;
- newCapacity |= newCapacity >>> 8;
- newCapacity |= newCapacity >>> 16;
- newCapacity ++;
- Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
- Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
- newArray[index] = value;
- indexedVariables = newArray;
- }
- // remove方法直接在对应位置上设置UNSET
- public Object removeIndexedVariable(int index) {
- Object[] lookup = indexedVariables;
- if (index < lookup.length) {
- Object v = lookup[index];
- lookup[index] = UNSET;
- return v;
- } else {
- return UNSET;
- }
- }
- public boolean isIndexedVariableSet(int index) {
- Object[] lookup = indexedVariables;
- return index < lookup.length && lookup[index] != UNSET;
- }
知道了上面这一段原理的介绍,后面的代码已经不重要了,不过我们还是来八一八吧,时间不多的同学可以选择不往下看了(虽然后面还有一个重要的点)。
先看看几个辅助的方法:
- // 该方法与get()不同的是,get()在查找不到数据是会进行初始化,而这个方法则是直接返回
- public static InternalThreadLocalMap getIfSet() {
- Thread thread = Thread.currentThread();
- InternalThreadLocalMap threadLocalMap;
- if (thread instanceof FastThreadLocalAccess) {
- threadLocalMap = ((FastThreadLocalAccess) thread).threadLocalMap();
- } else {
- ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
- if (slowThreadLocalMap == null) {
- threadLocalMap = null;
- } else {
- threadLocalMap = slowThreadLocalMap.get();
- }
- }
- return threadLocalMap;
- }
- public static InternalThreadLocalMap get() {
- Thread thread = Thread.currentThread();
- if (thread instanceof FastThreadLocalAccess) {
- // 如果实现了FastThreadLocalAccess,则采用netty的算法
- return fastGet((FastThreadLocalAccess) thread);
- } else {
- // 没有未实现FastThreadLocalAccess,则降级使用jdk的ThreadLocal来存放InternalThreadLocalMap
- return slowGet();
- }
- }
- private static InternalThreadLocalMap fastGet(FastThreadLocalAccess thread) {
- InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
- if (threadLocalMap == null) {
- thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
- }
- return threadLocalMap;
- }
- private static InternalThreadLocalMap slowGet() {
- // slowGet采用了java的ThreadLocal来管理InternalThreadLocalMap;
- // 整个方案相对于直接用ThreadLocal来说又多了一步InternalThreadLocalMap的查询,但差距很小。
- // 好处是统一了FastThreadLocal的模型,同时上文中也提到InternalThreadLocalMap做了很多功能上的增强。
- ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
- if (slowThreadLocalMap == null) {
- UnpaddedInternalThreadLocalMap.slowThreadLocalMap =
- slowThreadLocalMap = new ThreadLocal<InternalThreadLocalMap>();
- }
- InternalThreadLocalMap ret = slowThreadLocalMap.get();
- if (ret == null) {
- ret = new InternalThreadLocalMap();
- slowThreadLocalMap.set(ret);
- }
- return ret;
- }
- private final int index;
- public FastThreadLocal() {
- // 初始化时分配一个全局唯一的index
- index = InternalThreadLocalMap.nextVariableIndex();
- }
- public final V get() {
- // InternalThreadLocalMap.get()获取到与当前线程关联的InternalThreadLocalMap, 通过该map来查询具体数据
- return get(InternalThreadLocalMap.get());
- }
- public final V get(InternalThreadLocalMap threadLocalMap) {
- // 直接采用index下标访问threadLocalMap中数组的指定位置元素
- Object v = threadLocalMap.indexedVariable(index);
- if (v != InternalThreadLocalMap.UNSET) {
- return (V) v;
- }
- // 不存在值则初始化
- return initialize(threadLocalMap);
- }
- private V initialize(InternalThreadLocalMap threadLocalMap) {
- V v = null;
- try {
- v = initialValue();
- } catch (Exception e) {
- PlatformDependent.throwException(e);
- }
- // 初始化后再设置,下次就不用再初始化
- threadLocalMap.setIndexedVariable(index, v);
- addToVariablesToRemove(threadLocalMap, this);
- return v;
- }
- /**
- * Set the value for the current thread.
- */
- public final void set(V value) {
- if (value != InternalThreadLocalMap.UNSET) {
- set(InternalThreadLocalMap.get(), value);
- } else {
- remove();
- }
- }
- /**
- * Set the value for the specified thread local map. The specified thread local map must be for the current thread.
- */
- public final void set(InternalThreadLocalMap threadLocalMap, V value) {
- // UNSET表示移除
- if (value != InternalThreadLocalMap.UNSET) {
- if (threadLocalMap.setIndexedVariable(index, value)) {
- addToVariablesToRemove(threadLocalMap, this);
- }
- } else {
- remove(threadLocalMap);
- }
- }
- /**
- * Sets the value to uninitialized; a proceeding call to get() will trigger a call to initialValue().
- */
- public final void remove() {
- remove(InternalThreadLocalMap.getIfSet());
- }
- /**
- * Sets the value to uninitialized for the specified thread local map;
- * a proceeding call to get() will trigger a call to initialValue().
- * The specified thread local map must be for the current thread.
- */
- @SuppressWarnings("unchecked")
- public final void remove(InternalThreadLocalMap threadLocalMap) {
- if (threadLocalMap == null) {
- return;
- }
- // 直接使用下标进行remove
- Object v = threadLocalMap.removeIndexedVariable(index);
- removeFromVariablesToRemove(threadLocalMap, this);
- if (v != InternalThreadLocalMap.UNSET) {
- try {
- onRemoval((V) v);
- } catch (Exception e) {
- PlatformDependent.throwException(e);
- }
- }
- }
- private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();
- 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);
- }
- private static void removeFromVariablesToRemove(
- InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
- Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
- if (v == InternalThreadLocalMap.UNSET || v == null) {
- return;
- }
- @SuppressWarnings("unchecked")
- Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v;
- variablesToRemove.remove(variable);
- }
- /**
- * 移除绑定到该线程的所有变量. 当不希望保留你无法管理的本地变量时使用该方法做清理。
- */
- public static void removeAll() {
- InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
- if (threadLocalMap == null) {
- return;
- }
- try {
- Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
- if (v != null && v != InternalThreadLocalMap.UNSET) {
- // 取出Set中的所有FastThreadLocal进行清理
- Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v;
- FastThreadLocal<?>[] variablesToRemoveArray =
- variablesToRemove.toArray(new FastThreadLocal[variablesToRemove.size()]);
- for (FastThreadLocal<?> tlv: variablesToRemoveArray) {
- tlv.remove(threadLocalMap);
- }
- }
- } finally {
- InternalThreadLocalMap.remove();
- }
- }