FastThreadLocal
[TOC]
一、背景
因为需要,研究了可以通过InheritableThreadLocal
进行父子线程中如何传递本地线程变量,通过阿里开源项目TransmitableThreadLocal
进行进行线程池传递本地线程变量(详解可查看以往博客)。在查找资料的过程中无意发现了Dobbo
的InternalThreadLocal
,其实Dobbo
的InternalThreadLocal
和netty
的FastThreadLocal
有异曲同工之妙。之前学netty
的时候有了解一点,为了加深一下ThreadLocal
种群的了解,使用本博客记录一下。
二、实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public static void main(String[] args) {
new Thread(new Runnable() { @Override public void run() { username.set("zhangSang"); password.set("123456"); password.remove();
System.out.println(username.get()); System.out.println(password.get()); } }).start();
new FastThreadLocalThread(new Runnable() { @Override public void run() { username.set("zhangSang"); password.set("123456");
System.out.println(username.get()); System.out.println(password.get()); } }).start(); }
|
单从运行结果看,都能获取到对应设置的值,二者没有任何输出区别,但是跟踪一下,可以看到调用的逻辑是有所区别的。
三、原理
熟悉ThreadLocal
的应该知道,Threadlocal
其实内部逻辑是一个以ThreadLocal
对象为key
,需要存储的值为value
的map
结构。而FastThreadLocal
的内部实现是一个数组,实现上是直接通过下标定位元素,所以单纯从取、存的角度看,FastThreadLocal
是比ThreadLocal
高效。
对于使用原生线程Thread
来说,其实最后是将数据存Thread.threadLocals(ThreadLocal.ThreadLocalMap)
中,也就是说在这个线程所使用的所有FastThreadLocal
最后都以 key=ThreadLocal<InternalThreadLocalMap>
对象,value=InternalThreadLocalMap
的方式存在线程ThreadLocal
的一个节点中。而若使用的是netty
封装的FastThreadLocalThread
,则FastThreadLocalThread
对象的属性threadLocalMap
中。
FastThreadLocalThread
是直接继承于线程类Thread
,并且内部维护一个InternalThreadLocalMap
,用于存储变量。虽然这个类命名为Map
,结构上其实是一个数组。并且下标为0的元素是一个Set<FastThreadLocal<?>>
的结构,存储着当前有效的FastThreadLocal
。
1 2 3
| public class FastThreadLocalThread extends Thread{ private InternalThreadLocalMap threadLocalMap; }
|
在InternalThreadLocalMap
中提供了一些静态方法,通过当前线程的不同类型,以不同的方式获取对应所需要的InternalThreadlocalMap
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
public static InternalThreadLocalMap get() { Thread thread = Thread.currentThread(); if (thread instanceof FastThreadLocalThread) { return fastGet((FastThreadLocalThread) thread); } else { return slowGet(); } }
private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) { InternalThreadLocalMap threadLocalMap = thread.threadLocalMap(); if (threadLocalMap == null) { thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap()); } return threadLocalMap; }
private static InternalThreadLocalMap slowGet() {
ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap; InternalThreadLocalMap ret = slowThreadLocalMap.get(); if (ret == null) { ret = new InternalThreadLocalMap(); slowThreadLocalMap.set(ret); } return ret; }
|
1.set值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| 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); } }
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); }
|
2.get值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 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); }
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; }
|
3.remove
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public final void remove() { remove(InternalThreadLocalMap.getIfSet()); }
@SuppressWarnings("unchecked") public final void remove(InternalThreadLocalMap threadLocalMap) { if (threadLocalMap == null) { return; } Object v = threadLocalMap.removeIndexedVariable(index); removeFromVariablesToRemove(threadLocalMap, this); if (v != InternalThreadLocalMap.UNSET) { try { onRemoval((V) v); } catch (Exception e) { PlatformDependent.throwException(e); } } }
|
四、总结
FastThreadLocal
实际上采用的是数组的方式进行存储数据,在数据的获取、赋值都是通过下标的方式进行,而ThreadLocal
是通过map
结构,先计算哈希值,在进行线性探测的方式进行定位。所以在高并发下,FastThreadLocal
应该相对高效,但是FastThread
有一个弊端就是index
是一直累加,也就是说如果移除了某个变量是通过将对应下标的元素标记为UNSET
占位,而不进行回收,会无限制增大,会触发扩容等一些问题。