1. 前言

netty自行封装了FastThreadLocal以替换jdk提供的ThreadLocal,结合封装的FastThreadLocalThread,在多线程环境下的变量提高了ThreadLocal对象的查询以及更新效率.

下文,将通过对比ThreadLocalFastThreadLocal,通过源码解析,探究FastThreadLocalFastThreadLocalThread的搭配使用后性能的奥秘.

2. ThreadLocalMap

ThreadLocalMapTharedLocal中定义的静态类,其作用是保存Thared中引用的ThreadLocal对象.

jdk中,每一个Thread对象中均会包含以下两个变量:

  1. public
  2. class Thread implements Runnable {
  3. // 此处省略若干代码
  4. // 存储ThreadLocal变量,通过每个Thread存储一个ThreadLocalMap,实现了变量的线程隔离
  5. ThreadLocal.ThreadLocalMap threadLocals = null;
  6. ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
  7. }

编程实践中,线程中可能包含多个ThreadLocal去进行引用,它们均保存在ThreadLocal.ThreadLocalMap threadLocals中(每个线程中均包含自己的ThreadLocalMap,避免多线程争用).

  1. static class ThreadLocalMap {
  2. // 需要注意,此处Entry使用WeakReference
  3. (软引用),这样在资源紧张的时候可以回收部分不再引用的ThreadLocal变量
  4. static class Entry extends WeakReference<ThreadLocal<?>> {
  5. /** The value associated with this ThreadLocal. */
  6. Object value;
  7. Entry(ThreadLocal<?> k, Object v) {
  8. super(k);
  9. value = v;
  10. }
  11. }
  12. // ThreadLocal对象存储数组的初始化长度
  13. private static final int INITIAL_CAPACITY = 16;
  14. // ThreadLocal对象存储数组
  15. private Entry[] table;
  16. // 初始化ThreadLocalMap,使用数组存放ThreadLocal资源,使用ThreadLocal对象的threadLocalHashCode进行hash得到索引
  17. // 此处使用对象数组存放ThreadLocal对象,操作类似于HashMap,感兴趣的读者可以查看HashMap的源码进行比较
  18. ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
  19. table = new Entry[INITIAL_CAPACITY];
  20. int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
  21. table[i] = new Entry(firstKey, firstValue);
  22. size = 1;
  23. setThreshold(INITIAL_CAPACITY);
  24. }
  25. // 获取ThreadLocal对象,此处需要根据threadLocalHashCode进行hash操作得到索引
  26. private Entry getEntry(ThreadLocal<?> key) {
  27. int i = key.threadLocalHashCode & (table.length - 1);
  28. Entry e = table[i];
  29. if (e != null && e.get() == key)
  30. return e;
  31. else
  32. return getEntryAfterMiss(key, i, e);
  33. }
  34. }

由以上代码可知,在ThreadLocalMap初始化时,会创建一个对象数组.

对象数组的初始长度为16,在后续的扩张中,数组长度会保持在2^n级别,以便进行hash操作确定ThradLocal对象的索引.

在每次获取ThreadLocal对象的时候,会根据对象的threadLocalHashCode与对象数组长度减一的求与值,确定对象索引,从而快速获取value.

使用hash确定数组下标,存在以下几个问题:

  • 解决hash冲突;
  • 对象数组扩容带来的rehash.

ThreadLocaljdk提供的通用类,在大部分场景下,线程中的ThreadLocal变量较少,因此hash冲突以及rehash较少.

即使,偶尔发生的hash冲突以及rehash,也不会给应用程序带来较大的性能损耗.

3. FastThreadLocalThread

NettyThreadLocal改造为FastThreadLocal,以应对自身的大并发量,数据吞吐量大的应用场景.

为了更好的使用,Netty亦继承Thread,构建了FastThreadLocalThread.

当且仅当FastThreadLocalFastThreadLocalThread合并使用,方能真正起到提速的作用.

  1. // 限于篇幅,省略较多函数
  2. public class FastThreadLocalThread extends Thread {
  3. // 相对于Thread中使用ThreadLocal.ThreadLocalMap存放ThreadLocal资源,FastThreadLocalThread使用InternalThreadLocalMap存放ThreadLocal资源
  4. private InternalThreadLocalMap threadLocalMap;
  5. public final InternalThreadLocalMap threadLocalMap() {
  6. return threadLocalMap;
  7. }
  8. public final void setThreadLocalMap(InternalThreadLocalMap threadLocalMap) {
  9. this.threadLocalMap = threadLocalMap;
  10. }
  11. @UnstableApi
  12. public boolean willCleanupFastThreadLocals() {
  13. return cleanupFastThreadLocals;
  14. }
  15. @UnstableApi
  16. public static boolean willCleanupFastThreadLocals(Thread thread) {
  17. return thread instanceof FastThreadLocalThread &&
  18. ((FastThreadLocalThread) thread).willCleanupFastThreadLocals();
  19. }
  20. }

由以上代码可以看出,相对于Thread,FastThreadLocalThread添加了threadLocalMap对象,以及threadLocalMap的清理标志获取函数.

ThreadLocal即使使用了WeakReference以保证资源释放,但是仍会存在内存泄漏可能.

FastThreadLocalThreadFastThreadLocal均为Netty定制,可以在线程任务执行后,强制执行InternalThreadLocalMap的清理函数removeAll(详情见下文).

4. FastThreadLocal

4.1 InternalThreadLocalMap

前情提要:

FastThreadLocalThread中声明了InternalThreadLocalMap对象threadLocalMap.

  1. public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap{
  2. }

从以上代码可知,InternalThreadLocalMap继承于UnpaddedInternalThreadLocalMap.

因此,我们需要先探究下UnpaddedInternalThreadLocalMap的定义.

  1. //
  2. class UnpaddedInternalThreadLocalMap {
  3. // 如果在`Thread`中使用`FastThreadLocal`,则实际上使用`ThreadLocal`存放资源
  4. static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = new ThreadLocal<InternalThreadLocalMap>();
  5. // 资源索引,每一个FastThreadLocal对象都会有对应的ID,即通过nextIndex自增得到
  6. static final AtomicInteger nextIndex = new AtomicInteger();
  7. // FastThreadLocal的资源存放地址,ThreadLocal中是通过ThreadLocalMap存放资源,索引是ThreadLocal对象的threadLocalHashCode进行hash得到
  8. // FastThreadLocal使用Object[]数组,使用通过nextIndex自增得到的数值作为索引,保证每次查询数值都是O(1)操作
  9. // 需要注意,FastThreadLocal对象为了避免伪共享带来的性能损耗,使用padding使得FastThreadLocal的对象大小超过128byte
  10. // 避免伪共享的情况下,indexedVariables的多个连续数值在不更新的前提下可以被缓存至cpu chache line中,这样大大的提高了查询效率
  11. Object[] indexedVariables;
  12. // Core thread-locals
  13. int futureListenerStackDepth;
  14. int localChannelReaderStackDepth;
  15. Map<Class<?>, Boolean> handlerSharableCache;
  16. IntegerHolder counterHashCode;
  17. ThreadLocalRandom random;
  18. Map<Class<?>, TypeParameterMatcher> typeParameterMatcherGetCache;
  19. Map<Class<?>, Map<String, TypeParameterMatcher>> typeParameterMatcherFindCache;
  20. // String-related thread-locals
  21. StringBuilder stringBuilder;
  22. Map<Charset, CharsetEncoder> charsetEncoderCache;
  23. Map<Charset, CharsetDecoder> charsetDecoderCache;
  24. // ArrayList-related thread-locals
  25. ArrayList<Object> arrayList;
  26. // 构造函数,后续需要关注
  27. UnpaddedInternalThreadLocalMap(Object[] indexedVariables) {
  28. this.indexedVariables = indexedVariables;
  29. }
  30. }

以上代码中,需要注意:

  1. static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = new ThreadLocal<InternalThreadLocalMap>();

声明slowThreadLocalMap的原因在于,用户可能在Thread而非FastThreadLocalThread中调用FastThreadLocal.

因此,为了保证程序的兼容性,声明此变量保存普通的ThreadLocal相关变量(具体使用详见后面说明).


  1. // 出于篇幅考虑,删除部分函数
  2. public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
  3. private static final int DEFAULT_ARRAY_LIST_INITIAL_CAPACITY = 8;
  4. // 资源未赋值变质量
  5. public static final Object UNSET = new Object();
  6. // 获取ThreadLocal对象,此处会判断当前调用线程的类型分别调用不同的资源
  7. public static InternalThreadLocalMap getIfSet() {
  8. Thread thread = Thread.currentThread();
  9. if (thread instanceof FastThreadLocalThread) {
  10. return ((FastThreadLocalThread) thread).threadLocalMap();
  11. }
  12. return slowThreadLocalMap.get();
  13. }
  14. // 获取ThreadLocal对象,此处会判断当前调用线程的类型,从而判断调用fastGet或是slowGet
  15. public static InternalThreadLocalMap get() {
  16. Thread thread = Thread.currentThread();
  17. if (thread instanceof FastThreadLocalThread) {
  18. return fastGet((FastThreadLocalThread) thread);
  19. } else {
  20. return slowGet();
  21. }
  22. }
  23. // 如果当前调用FastThreadLocal对象的是FastThreadLocalThread,则调用FastThreadLocalThread的threadLocalMap对象获取相关资源
  24. private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
  25. InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
  26. if (threadLocalMap == null) {
  27. thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
  28. }
  29. return threadLocalMap;
  30. }
  31. // 如果当前调用FastThreadLocal对象的是Thread,则调用slowThreadLocalMap对象获取相关资源(slowThreadLocalMap其实是调用jdk提供的ThreadLocalMap)
  32. private static InternalThreadLocalMap slowGet() {
  33. ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
  34. InternalThreadLocalMap ret = slowThreadLocalMap.get();
  35. if (ret == null) {
  36. ret = new InternalThreadLocalMap();
  37. slowThreadLocalMap.set(ret);
  38. }
  39. return ret;
  40. }
  41. // 保证FastThreadLocal的实体对象大小超过128byte,以避免伪共享发生
  42. // 如果资源能够避免伪共享,则FastThreadLocal的实体对象能够部分缓存至L1缓存,通过提高缓存命中率加快查询速度(查询L1缓存的速度要远快于查询主存速度)
  43. // 更多解释,详见
  44. public long rp1, rp2, rp3, rp4, rp5, rp6, rp7, rp8, rp9;
  45. private InternalThreadLocalMap() {
  46. super(newIndexedVariableTable());
  47. }
  48. // 初始化资源,初始化的长度为32,并初始化为UNSET
  49. private static Object[] newIndexedVariableTable() {
  50. Object[] array = new Object[32];
  51. Arrays.fill(array, UNSET);
  52. return array;
  53. }
  54. }

以上代码为InternalThreadLocalMap的主要实现,对于使用者来说,需要关注以下几个函数:

  • getIfSet();
  • get();
  • fastGet();
  • slowGet();

存在以下两种情况:

(1) 在Thread中调用FastThreadLocal;

(2) 在FastThreadLocalThread中调用FastThreadLocal.

因为存在以上两种调用场景,在获取InternalThreadLocalMap时,会使用instanceof进行判断,如下所示:

  1. if (thread instanceof FastThreadLocalThread) {
  2. // 对应fastGet等操作
  3. } else {
  4. // 对应slowGet等操作
  5. }

如果调用线程是

  • Thread: 调用UnpaddedInternalThreadLocalMap中的slowThreadLocalMap变量;
  • FastThreadLocalThread: 调用FastThreadLocalThread中的threadLocalMap变量.

因为InternalThreadLocalMap构造函数为私有函数,所以在getIfSet/fastGet函数中均是获取FastThreadLocalThreadthreadLocalMap变量.若变量为空,则调用私有构造函数进行赋值操作.

  1. // Cache line padding (must be public)
  2. // With CompressedOops enabled, an instance of this class should occupy at least 128 bytes.
  3. public long rp1, rp2, rp3, rp4, rp5, rp6, rp7, rp8, rp9;
  4. private InternalThreadLocalMap() {
  5. super(newIndexedVariableTable());
  6. }
  7. private static Object[] newIndexedVariableTable() {
  8. Object[] array = new Object[32];
  9. Arrays.fill(array, UNSET);
  10. return array;
  11. }

构造函数,会创建一个Object数组(初始化长度为32),并逐个初始化数值为UNSET,为后续的赋值操作提供判断依据(详见removeIndexedVariable以及isIndexedVariableSet函数).

Tips:

构造函数存在一段代码public long rp1, rp2, rp3, rp4, rp5, rp6, rp7, rp8, rp9;.

此段代码无实际实用意义,其存在是为了保证InternalThreadLocalMap的实例大小超过128字节(以上long变量72字节,InternalThreadLocalMap的基类UnpaddedInternalThreadLocalMap亦存在若干变量).

cpu cache line的大小一般为64k或者128k,变量的大小超过128byte,则会极大的减少伪共享情况.

(当前Netty的版本号是4.1.38InternalThreadLocalMap的实例大小是136byte,这是因为在Netty的4.0.33版本后,引入了cleanerFlags 以及arrayList变量,忘记去除rp9变量导致的).

关于伪共享,可关注JAVA 拾遗 — CPU Cache 与缓存行一文.

4.2 FastThreadLocal初始化

  1. public class FastThreadLocal<V> {
  2. private final int index;
  3. // 原子变量自增,获取ID,作为FastThreadLocal的存放索引
  4. // public static int nextVariableIndex() {
  5. // int index = nextIndex.getAndIncrement();
  6. // if (index < 0) {
  7. // nextIndex.decrementAndGet();
  8. // throw new IllegalStateException("too many thread-local indexed variables");
  9. // }
  10. // return index;
  11. // }
  12. public FastThreadLocal() {
  13. index = InternalThreadLocalMap.nextVariableIndex();
  14. }
  15. // 设置FastThreadLocal资源
  16. public final void set(V value) {
  17. if (value != InternalThreadLocalMap.UNSET) {
  18. InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
  19. setKnownNotUnset(threadLocalMap, value);
  20. } else {
  21. // 如果设置的资源为UNSET,则销毁当前FastThreadLocal对应的资源对象
  22. remove();
  23. }
  24. }
  25. // 设置资源,并将设置好的FastThreadLocal变量添加至待销毁资源列表中,待后续进行销毁操作
  26. private void setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {
  27. if (threadLocalMap.setIndexedVariable(index, value)) {
  28. addToVariablesToRemove(threadLocalMap, this);
  29. }
  30. }
  31. // 根据FastThreadLocal初始化的index,确定其在资源列表中的位置,后续查询资源就可以根据索引快速确定位置
  32. public boolean setIndexedVariable(int index, Object value) {
  33. Object[] lookup = indexedVariables;
  34. if (index < lookup.length) {
  35. Object oldValue = lookup[index];
  36. lookup[index] = value;
  37. return oldValue == UNSET;
  38. } else {
  39. expandIndexedVariableTableAndSet(index, value);
  40. return true;
  41. }
  42. }
  43. // 按照2的倍数,扩张资源池数组长度
  44. private void expandIndexedVariableTableAndSet(int index, Object value) {
  45. Object[] oldArray = indexedVariables;
  46. final int oldCapacity = oldArray.length;
  47. int newCapacity = index;
  48. newCapacity |= newCapacity >>> 1;
  49. newCapacity |= newCapacity >>> 2;
  50. newCapacity |= newCapacity >>> 4;
  51. newCapacity |= newCapacity >>> 8;
  52. newCapacity |= newCapacity >>> 16;
  53. newCapacity ++;
  54. Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
  55. Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
  56. newArray[index] = value;
  57. indexedVariables = newArray;
  58. }
  59. }

以上是FastThreadLocal的部分函数节选.

由构造函数可知,FastThreadLocal在初始化的时候,会使用InternalThreadLocalMapnextVariableIndex获取一个唯一ID.

ID为原子变量自增获取,后续对此变量的更新或者删除操作,均是通过此index进行操作.

在设置变量的时候,存在indexedVariables空间不足的情况(初始化长度为32),则会对此数组通过expandIndexedVariableTableAndSet进行扩容操作(>>>为无符号右移即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0).通过这样的位移操作,每次数组均会乘2(保持2^n).

因为使用常数索引index,因此Netty中查询FastThreadLocal变量的速度为O(1),扩容时采用Arrays.Copy也很简单(相较于jdkThreadLocalrehash操作).

4.3 FastThreadLocal变量获取及删除

  1. public class FastThreadLocal<V> {
  2. private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();
  3. // 在线程执行完资源之后,需要根据业务场景,确定是否调用此函数以销毁线程中存在的FastThreadLocal资源
  4. public static void removeAll() {
  5. InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
  6. if (threadLocalMap == null) {
  7. return;
  8. }
  9. try {
  10. Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
  11. if (v != null && v != InternalThreadLocalMap.UNSET) {
  12. @SuppressWarnings("unchecked")
  13. Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v;
  14. FastThreadLocal<?>[] variablesToRemoveArray =
  15. variablesToRemove.toArray(new FastThreadLocal[0]);
  16. for (FastThreadLocal<?> tlv: variablesToRemoveArray) {
  17. tlv.remove(threadLocalMap);
  18. }
  19. }
  20. } finally {
  21. // 实际上仅仅是将FastThreadLocalThread中的threadLocalMap置为null,或者是将slowThreadLocalMap销毁
  22. InternalThreadLocalMap.remove();
  23. }
  24. }
  25. @SuppressWarnings("unchecked")
  26. public final V get(InternalThreadLocalMap threadLocalMap) {
  27. Object v = threadLocalMap.indexedVariable(index);
  28. if (v != InternalThreadLocalMap.UNSET) {
  29. return (V) v;
  30. }
  31. // 如果当前待获取资源为空,则进行初始操作,返回相应资源
  32. return initialize(threadLocalMap);
  33. }
  34. // 根据用户重载的initialValue函数,初始化待获取资源
  35. private V initialize(InternalThreadLocalMap threadLocalMap) {
  36. V v = null;
  37. try {
  38. v = initialValue();
  39. } catch (Exception e) {
  40. PlatformDependent.throwException(e);
  41. }
  42. threadLocalMap.setIndexedVariable(index, v);
  43. addToVariablesToRemove(threadLocalMap, this);
  44. return v;
  45. }
  46. // 将FastThreadLocal变量,添加至待删除的资源列表中
  47. @SuppressWarnings("unchecked")
  48. private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
  49. Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
  50. Set<FastThreadLocal<?>> variablesToRemove;
  51. // 如果待删除资源列表为空,则初始化待删除资源列表(Set)
  52. if (v == InternalThreadLocalMap.UNSET || v == null) {
  53. variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>());
  54. threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove);
  55. } else {
  56. variablesToRemove = (Set<FastThreadLocal<?>>) v;
  57. }
  58. variablesToRemove.add(variable);
  59. }
  60. @SuppressWarnings("unchecked")
  61. public final void remove(InternalThreadLocalMap threadLocalMap) {
  62. if (threadLocalMap == null) {
  63. return;
  64. }
  65. Object v = threadLocalMap.removeIndexedVariable(index);
  66. removeFromVariablesToRemove(threadLocalMap, this);
  67. // FastThreadLocal变量已经被赋值,则需要调用用户重载的onRemoval函数,销毁资源
  68. if (v != InternalThreadLocalMap.UNSET) {
  69. try {
  70. onRemoval((V) v);
  71. } catch (Exception e) {
  72. PlatformDependent.throwException(e);
  73. }
  74. }
  75. }
  76. // 确定资源的初始化函数(如果用户不进行重载,则返回null)
  77. protected V initialValue() throws Exception {
  78. return null;
  79. }
  80. // 用户需要重载次函数,以便销毁申请的资源
  81. protected void onRemoval(@SuppressWarnings("UnusedParameters") V value) throws Exception { }
  82. }

用户在使用FastThreadLocal时,需要继承initialValue以及onRemoval函数(FastThreadLocal对象的初始化及销毁交由用户控制).

  • initialValue: 在获取FastThreadLocal对象时,若对象未设置,则调用initialValue初始化资源(get等函数中判断对象为空,则调用initialize初始化资源);
  • onRemoval: 在FastThreadLocal更新对象或最终销毁资源时,调用onRemoval销毁资源(set等函数中判断待设置对象已被设置过,则调用onRemoval销毁资源).
  1. this.threadLocal = new FastThreadLocal<Recycler.Stack<T>>() {
  2. protected Recycler.Stack<T> initialValue() {
  3. return new Recycler.Stack(Recycler.this, Thread.currentThread(), Recycler.this.maxCapacityPerThread, Recycler.this.maxSharedCapacityFactor, Recycler.this.ratioMask, Recycler.this.maxDelayedQueuesPerThread);
  4. }
  5. protected void onRemoval(Recycler.Stack<T> value) {
  6. if (value.threadRef.get() == Thread.currentThread() && Recycler.DELAYED_RECYCLED.isSet()) {
  7. ((Map)Recycler.DELAYED_RECYCLED.get()).remove(value);
  8. }
  9. }
  10. };

以上代码,就是Recycler调用FastThreadLocal的使用示范(RecyclerNetty的轻量级对象池).

需要注意,在FastThreadLocal中,存在一个静态变量variablesToRemoveIndex,其作用是在对象池中占据一个固定位置,存放一个集合Set<FastThreadLocal<?>> variablesToRemove.

每次初始化变量的时候,均会将对应的FastThreadLocal存放至variablesToRemove中,在更新对象的时候(set等函数)或者清理FastThreadLocalThread中的变量时(removeAll函数)时,程序就会根据variablesToRemove进行相应的清理工作.

这样,用户在使用FastThreadLocalThread时,就无须花费过多的经理关注线程安全问题(在Netty中,线程池的生命周期较长,无需过多的关注内存清理,然而如果用户在线程池等场景使用FastThreadLocalThread,就需要在执行完任务后,清理FastThreadLocal参数,以免对后续的业务产生影响).

总结

通过以上源码分析,可以得知Netty为了提升ThreadLocal性能,做了很多改善操作.

  • 定制FastThreadLocalThread以及FastThreadLocal;
  • 使用padding手段扩充FastThreadLocal的实例大小,避免伪共享;
  • 使用原子变量自增获取的ID作为常数索引,优化查询速度至O(1),避免了hash冲突以及扩容导致的rehash操作;
  • 提供initialValue以及onRemoval函数,用户可以自行重载函数,实现FastThreadLocal资源的高度定制化操作;
  • FastThreadLocal对象数组的扩容(expandIndexedVariableTableAndSet)采用位操作,计算数组长度;
  • 针对在Thread中调用FastThreadLocal以及在FastThreadLocalThread中调用FastThreadLocal,分别采用不同的获取方式,增强了兼容性.
  • 更多细节,读者可以自己参照源码进行进一步分析.

对于采用Object[]数组存放FastThreadLocal变量,是否存在牺牲空间换取性能,个人理解如下:

Netty的默认启动线程是2 * cpu core,也就是两倍cpu核数,且此线程组会在Netty的生命周期中持续存在.

Netty不存在创建过多线程导致内存占用过多的现象(用户手动调节Nettyboss group以及worker group线程数量都会很慎重).

此外,Netty中对于FastThreadLocal存在较大的读取以及更新需求量,确实存在优化ThreadLocal的需求.

因此,适当的浪费一些空间,换取查询和更新的性能提升,是恰当的操作.

PS:

如果您觉得我的文章对您有帮助,请关注我的微信公众号,谢谢!

Netty高性能组件——FastThreadLocal源码解析(细微处见真章)的更多相关文章

  1. .Net Core缓存组件(Redis)源码解析

    上一篇文章已经介绍了MemoryCache,MemoryCache存储的数据类型是Object,也说了Redis支持五中数据类型的存储,但是微软的Redis缓存组件只实现了Hash类型的存储.在分析源 ...

  2. .Net Core缓存组件(MemoryCache)源码解析

    一.介绍 由于CPU从内存中读取数据的速度比从磁盘读取快几个数量级,并且存在内存中,减小了数据库访问的压力,所以缓存几乎每个项目都会用到.一般常用的有MemoryCache.Redis.MemoryC ...

  3. Django 之 admin组件使用&源码解析

    admin组件使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.可以在项目的 settings.py 中的 INSTALLED ...

  4. Java并发包源码学习系列:同步组件CountDownLatch源码解析

    目录 CountDownLatch概述 使用案例与基本思路 类图与基本结构 void await() boolean await(long timeout, TimeUnit unit) void c ...

  5. Java并发包源码学习系列:同步组件CyclicBarrier源码解析

    目录 CyclicBarrier概述 案例学习 类图结构及重要字段 内部类Generation及相关方法 void reset() void breakBarrier() void nextGener ...

  6. Java并发包源码学习系列:同步组件Semaphore源码解析

    目录 Semaphore概述及案例学习 类图结构及重要字段 void acquire() 非公平 公平策略 void acquire(int permits) void acquireUninterr ...

  7. Netty学习(三)高性能之ByteBuf源码解析

    原文链接: https://juejin.im/post/5db8ea506fb9a02061399ab3 Netty 的 ByteBuf 类型 Pooled(池化).Unpooled(非池化) Di ...

  8. Spring组件BeanDefinition 源码解析

    BeanDefinition 继承图 继承的接口 BeanMetadataElement接口 将由承载配置源对象的bean元数据元素的类实现. 包含一个getSource的方法,可以获取到MetaDa ...

  9. Flume-ng源码解析之Source组件

    如果你还没看过Flume-ng源码解析系列中的启动流程.Channel组件和Sink组件,可以点击下面链接: Flume-ng源码解析之启动流程 Flume-ng源码解析之Channel组件 Flum ...

随机推荐

  1. C#中增量类功能的方式之 继承与扩展

    之前一次公司培训的时候,将它记录下来,https://www.cnblogs.com/AlvinLee/p/10180536.html这个博客上面比较全面. 1.扩展方法 扩展方法是一种特殊的静态方法 ...

  2. flex三个对齐属性的记忆方式

    今天在群里聊天有人说 flex的那几个居中属性好难记,时不时都要尝试一下,或者查看一下文档,现在我把我自己的记忆方式分享一下... 1. flex的居中主要是通过这三个属性来实现的: justify- ...

  3. jenkins构建找不到python依赖

    最近在搞jenkins自动构建,遇到这样一个问题,我装的python相关的依赖在X用户下,但是jenkins在root下运行的,所以找不到相关的import 1. 执行pip freeze,会打印相关 ...

  4. appium+robotframework+python连接真机定位不到元素的问题处理

    这几天遇到了一个比较奇怪的问题,使用RF框架进行自动化测试的时候定位不到部分元素 并且这个元素的是有id的,更换了xpath定位也行不通,冥思苦想,加上谷歌百度,终于解决了 解决步骤如下: 1.定位问 ...

  5. python3 邮件方式发送测试报告

    以邮件方式发送测试报告 import smtplib from email.mime.text import MIMEText class SendEmail: """邮 ...

  6. Centos 静态网络配置

    cat /etc/sysconfig/network-scripts/ifcfg-ens33 DEFROUTE="yes" IPV4_FAILURE_FATAL="no& ...

  7. 201871010109-胡欢欢《面向对象程序设计(java)》第一周学习总结

    <面向对象程序设计(java)>第一周学习总结 正文开头: 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 ...

  8. vscode笔记

    一.修改操作栏字体 https://www.cnblogs.com/liuyangfirst/p/9759966.html 1.代码改写,进入默认安装的如下路径,搜索workbench 2.用Vs c ...

  9. 2019年最新50道java基础部分面试题(三)

    前21题请看之前的随笔 22.面向对象的特征有哪些方面 计算机软件系统是现实生活中的业务在计算机中的映射,而现实生活中的业务其实就是一个个对象协作的过程.面向对象编程就是按现实业务一样的方式将程序代码 ...

  10. 小程序-小菊花loading

    界面----交互 wx.showLoading() 显示loading提示框.需主动调用wx.hideLoading()才能关闭提示框 参数: 属性 类型 默认值 必填 说明 title string ...