FastThreadLocal
是 ThreadLocal
的一个变体,使用 FastThreadLocal
可获得更高的访问性能。
FastThreadLocal
需使用 FastThreadLocalThread
或其子类线程操作否则会更慢。
使用示例 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 public class FastThreadLocalTest { private static FastThreadLocal<Integer> fastThreadLocal = new FastThreadLocal<>(); public static void main (String[] args) { new FastThreadLocalThread(() -> { for (int i = 0 ; i < 10 ; i++) { fastThreadLocal.set(i); System.out.println(Thread.currentThread().getName() + "====" + fastThreadLocal.get()); try { Thread.sleep(2000 ); } catch (InterruptedException e) { e.printStackTrace(); } } }, "fastThreadLocal1" ).start(); new FastThreadLocalThread(() -> { for (int i = 0 ; i < 10 ; i++) { System.out.println(Thread.currentThread().getName() + "====" + fastThreadLocal.get()); try { Thread.sleep(2000 ); } catch (InterruptedException e) { e.printStackTrace(); } } }, "fastThreadLocal2" ).start(); } }
输出:
1 2 3 4 5 6 fastThreadLocal1====0 fastThreadLocal2====null fastThreadLocal2====null fastThreadLocal1====1 fastThreadLocal2====null fastThreadLocal1====2
源码分析 FastThreadLocal 使用了数组中的常量索引代替了 ThreadLocal 类中的 哈希和哈希表 实现,在请求频繁时这个改动效果会比较明显。使用的线程必须是 FastThreadLocalThread 或其子类。
1 2 3 4 5 6 7 8 9 10 11 public class FastThreadLocal <V > { private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex(); private final int index; public FastThreadLocal () { index = InternalThreadLocalMap.nextVariableIndex(); } }
创建第一个 FastThreadLocal 对象时,nextIndex为0;
创建第二个 FastThreadLocal 对象时,nextIndex为1;
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 public final V get () { InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get(); Object v = threadLocalMap.indexedVariable(index); if (v != InternalThreadLocalMap.UNSET) { return (V) v; } return initialize(threadLocalMap); } 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; } public final InternalThreadLocalMap threadLocalMap () { return threadLocalMap; } private InternalThreadLocalMap () { super (newIndexedVariableTable()); } private static Object[] newIndexedVariableTable() { Object[] array = new Object[INDEXED_VARIABLE_TABLE_INITIAL_SIZE]; Arrays.fill(array, UNSET); return array; } UnpaddedInternalThreadLocalMap(Object[] indexedVariables) { this .indexedVariables = indexedVariables; } private static InternalThreadLocalMap slowGet () { ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap; InternalThreadLocalMap ret = slowThreadLocalMap.get(); if (ret == null ) { ret = new InternalThreadLocalMap(); slowThreadLocalMap.set(ret); } return ret; } public Object indexedVariable (int index) { Object[] lookup = indexedVariables; return index < lookup.length? lookup[index] : UNSET; } 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; }
如果使用的是 FastThreadLocalThread
或其子类,才能获取到线程中的 threadLocalMap 属性
如果使用的是普通线程,则会使用普通的 ThreadLocal
。
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 37 38 39 40 41 42 43 44 45 46 public final void set (V value) { if (value != InternalThreadLocalMap.UNSET) { InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get(); setKnownNotUnset(threadLocalMap, value); } else { remove(); } } private void setKnownNotUnset (InternalThreadLocalMap threadLocalMap, V value) { if (threadLocalMap.setIndexedVariable(index, value)) { addToVariablesToRemove(threadLocalMap, this ); } } 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 { expandIndexedVariableTableAndSet(index, value); return true ; } } 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); }
remove方法 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 final void remove () { remove(InternalThreadLocalMap.getIfSet()); } public static InternalThreadLocalMap getIfSet () { Thread thread = Thread.currentThread(); if (thread instanceof FastThreadLocalThread) { return ((FastThreadLocalThread) thread).threadLocalMap(); } return slowThreadLocalMap.get(); } 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); } } } public Object removeIndexedVariable (int index) { Object[] lookup = indexedVariables; if (index < lookup.length) { Object v = lookup[index]; lookup[index] = UNSET; return v; } else { return UNSET; } }
清除数据 根据 FastThreadLocal 使用示例可以看出,我们在使用完 FastThreadLocal 并没有手动 remove 数据。这是因为 FastThreadLocalThread 会自动清理数据。
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 public FastThreadLocalThread (Runnable target, String name) { super (FastThreadLocalRunnable.wrap(target), name); cleanupFastThreadLocals = true ; } 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); } }
但如果没有使用 FastThreadLocalThread ,还是需要手动执行 removeAll() 方法,避免内存泄漏。
伪共享(False Sharing) 缓存行(通常是64个字节)是CPU同步的基本单位,缓存行隔离会比伪共享(False Sharing)效率要高 。
如果多个核的线程在操作同一个缓存行中的不同变量数据,那么就会出现频繁的缓存失效,即使在代码层面看这两个线程操作的数据之间完全没有关系。
这种不合理的资源竞争情况学名伪共享(False Sharing) ,会严重影响机器的并发执行效率。
InternalThreadLocalMap
为了进一步的提高效率在成员变量中添加了几个填充字段。这是为了防止伪共享:
1 2 3 public long rp1, rp2, rp3, rp4, rp5, rp6, rp7, rp8, rp9;
不过这里填充了9个long值,有点无法理解,github上也有人提出了 issues 。
性能测试 Netty 提供了相关的测试代码:地址
FastThreadLocalFastPathBenchmark
FastThreadLocalSlowPathBenchmark
使用 FastThreadLocalThread
操作 FastThreadLocal
吞吐量是 ThreadLocal
的3倍左右。
使用普通线程操作 FastThreadLocal
吞吐量比 ThreadLocal
还低。
小结
FastThreadLocal 使用常量下标来替代 ThreadLocal 通过哈希和哈希表操作元素。
FastThreadLocal 利用了缓存行的特性并使用缓存行填充解决了伪共享问题。
使用 FastThreadLocalThread 线程操作 FastThreadLocal 才会快,普通线程会更慢。
使用 FastThreadLocalThread 线程操作 FastThreadLocal 时,不需要手动执行 removeAll() 方法。
使用普通线程操作 FastThreadLocal 时,最后需要手动执行 removeAll() 方法。