目前关于FastThreadLocal的很多文章都有点老有点过时了(本文将澄清几个误区),很多文章关于FastThreadLocal介绍的也不全,希望本篇文章可以带你彻底理解FastThreadLocal!!!

FastThreadLocal是Netty提供的,在池化内存分配等都有涉及到!

关于FastThreadLocal,零度准备从这几个方面进行讲解:

  • FastThreadLocal的使用。
  • FastThreadLocal并不是什么情况都快,你要用对才会快。
  • FastThreadLocal利用字节填充来解决伪共享问题。
  • FastThreadLocal比ThreadLocal快,并不是空间换时间。
  • FastThreadLocal不在使用ObjectCleaner处理泄漏,必要的时候建议重写onRemoval方法。
  • FastThreadLocal为什么快?

FastThreadLocal的使用

FastThreadLocal用法上兼容ThreadLocal

FastThreadLocal使用示例代码截图:

代码运行结果:

我们在回顾下之前的ThreadLocal的最佳实践做法:

try { // 其它业务逻辑} finally { threadLocal对象.remove();}

备注:通过上面的例子,我们发现FastThreadLocal和ThreadLocal在用法上面基本差不多,没有什么特别区别,个人认为,这就是FastThreadLocal成功的地方,它就是要让用户用起来和ThreadLocal没啥区别,要兼容!

使用FastThreadLocal居然不用像ThreadLocal那样先try ………………… 之后finally进行threadLocal对象.remove();

由于构造FastThreadLocalThread的时候,通过FastThreadLocalRunnable对Runnable对象进行了包装:

FastThreadLocalRunnable.wrap(target)从而构造了FastThreadLocalRunnable对象。

FastThreadLocalRunnable在执行完之后都会调用FastThreadLocal.removeAll();

备注: FastThreadLocal不在使用ObjectCleaner处理泄漏,必要的时候建议重写onRemoval方法。关于这块将在本文后面进行介绍,这样是很多网上资料比较老的原因,这块已经去掉了。如果是普通线程,还是应该最佳实践:finally {fastThreadLocal对象.removeAll();}注意: 如果使用FastThreadLocal就不要使用普通线程,而应该构建FastThreadLocalThread,关于为什么这样,关于这块将在本文后面进行介绍:FastThreadLocal并不是什么情况都快,你要用对才会快。

FastThreadLocal并不是什么情况都快,你要用对才会快

首先看看netty关于这块的测试用例:

代码路径:https://github.com/netty/netty/blob/4.1/microbench/src/main/java/io/netty/microbench/concurrent/FastThreadLocalFastPathBenchmark.java

备注: 在我本地进行测试,FastThreadLocal的吞吐量是jdkThreadLocal的3倍左右。机器不一样,可能效果也不一样,大家可以自己试试,反正就是快了不少。

关于ThreadLocal,之前的这篇:手撕面试题ThreadLocal!!!已经详细介绍了。

FastThreadLocal并不是什么情况都快,你要用对才会快!!!

注意: 使用FastThreadLocalThread线程才会快,如果是普通线程还更慢!

注意: 使用FastThreadLocalThread线程才会快,如果是普通线程还更慢!

注意: 使用FastThreadLocalThread线程才会快,如果是普通线程还更慢!

netty的测试目录下面有2个类:

  • FastThreadLocalFastPathBenchmark
  • FastThreadLocalSlowPathBenchmark

路径:https://github.com/netty/netty/blob/4.1/microbench/src/main/java/io/netty/microbench/concurrent/

FastThreadLocalFastPathBenchmark测试结果:是ThreadLocal的吞吐量的3倍左右。

FastThreadLocalSlowPathBenchmark测试结果:比ThreadLocal的吞吐量还低。

测试结论:使用FastThreadLocalThread线程操作FastThreadLocal才会快,如果是普通线程还更慢!

注释里面给出了三点:

  • FastThreadLocal操作元素的时候,使用常量下标在数组中进行定位元素来替代ThreadLocal通过哈希和哈希表,这个改动特别在频繁使用的时候,效果更加显著!
  • 想要利用上面的特征,线程必须是FastThreadLocalThread或者其子类,默认DefaultThreadFactory都是使用FastThreadLocalThread的
  • 只用在FastThreadLocalThread或者子类的线程使用FastThreadLocal才会更快,因为FastThreadLocalThread 定义了属性threadLocalMap类型是InternalThreadLocalMap。如果普通线程会借助ThreadLocal。

我们看看NioEventLoopGroup细节:

看到这里,和刚刚我们看到的注释内容一致的,是使用FastThreadLocalThread的。

netty里面使用FastThreadLocal的举例常用的:

池化内存分配:

会使用到Recycler

而Recycler也使用了FastThreadLocal

我们再看看看测试类:

备注:我们会发现FastThreadLocalFastPathBenchmark里面的线程是FastThreadLocal。

备注:我们会发现FastThreadLocalSlowPathBenchmark里面的线程不是FastThreadLocal。

FastThreadLocal只有被的线程是FastThreadLocalThread或者其子类使用的时候才会更快,吞吐量我这边测试的效果大概3倍左右,但是如果是普通线程操作FastThreadLocal其吞吐量比ThreadLocal还差!

FastThreadLocal利用字节填充来解决伪共享问题

关于CPU 缓存 内容来源于美团:https://tech.meituan.com/2016/11/18/disruptor.html

下图是计算的基本结构。L1、L2、L3分别表示一级缓存、二级缓存、三级缓存,越靠近CPU的缓存,速度越快,容量也越小。所以L1缓存很小但很快,并且紧靠着在使用它的CPU内核;L2大一些,也慢一些,并且仍然只能被一个单独的CPU核使用;L3更大、更慢,并且被单个插槽上的所有CPU核共享;最后是主存,由全部插槽上的所有CPU核共享。

当CPU执行运算的时候,它先去L1查找所需的数据、再去L2、然后是L3,如果最后这些缓存中都没有,所需的数据就要去主内存拿。走得越远,运算耗费的时间就越长。所以如果你在做一些很频繁的事,你要尽量确保数据在L1缓存中。

另外,线程之间共享一份数据的时候,需要一个线程把数据写回主存,而另一个线程访问主存中相应的数据。

下面是从CPU访问不同层级数据的时间概念:

可见CPU读取主存中的数据会比从L1中读取慢了近2个数量级。

缓存行

Cache是由很多个cache line组成的。每个cache line通常是64字节,并且它有效地引用主内存中的一块儿地址。一个Java的long类型变量是8字节,因此在一个缓存行中可以存8个long类型的变量。

CPU每次从主存中拉取数据时,会把相邻的数据也存入同一个cache line。

在访问一个long数组的时候,如果数组中的一个值被加载到缓存中,它会自动加载另外7个。因此你能非常快的遍历这个数组。事实上,你可以非常快速的遍历在连续内存块中分配的任意数据结构。

伪共享

由于多个线程同时操作同一缓存行的不同变量,但是这些变量之间却没有啥关联,但是每次修改,都会导致缓存的数据变成无效,从而明明没有任何修改的内容,还是需要去主存中读(CPU读取主存中的数据会比从L1中读取慢了近2个数量级)但是其实这块内容并没有任何变化,由于缓存的最小单位是一个缓存行,这就是伪共享。

如果让多线程频繁操作的并且没有关系的变量在不同的缓存行中,那么就不会因为缓存行的问题导致没有关系的变量的修改去影响另外没有修改的变量去读主存了(那么从L1中取是从主存取快2个数量级的)那么性能就会好很多很多。

有伪共享 和没有的情况的测试效果

代码路径:https://github.com/jiangxinlingdu/nettydemo

nettydemo

利用字节填充来解决伪共享,从而速度快了3倍左右。

FastThreadLocal使用字节填充解决伪共享

之前介绍ThreadLocal的时候,说过ThreadLocal是用在多线程场景下,那么FastThreadLocal也是用在多线程场景,大家可以看下这篇:手撕面试题ThreadLocal!!!,所以FastThreadLocal需要解决伪共享问题,FastThreadLocal使用字节填充解决伪共享。

这个是我自己手算的,通过手算太麻烦,推荐一个工具JOL。

http://openjdk.java.net/projects/code-tools/jol/

推荐IDEA插件:https://plugins.jetbrains.com/plugin/10953-jol-java-object-layout

代码路径:https://github.com/jiangxinlingdu/nettydemo

nettydemo

通过这个工具算起来就很容易了,如果以后有类似的需要看的,不用手一个一个算了。

FastThreadLocal被FastThreadLocalThread进行读写的时候也可能利用到缓存行

并且由于当线程是FastThreadLocalThread的时候操作FastThreadLocal是通过indexedVariables数组进行存储数据的的,每个FastThreadLocal有一个常量下标,通过下标直接定位数组进行读写操作,当有很多FastThreadLocal的时候,也可以利用缓存行,比如一次indexedVariables数组第3个位置数据,由于缓存的最小单位是缓存行,顺便把后面的4、5、6等也缓存了,下次刚刚好另外FastThreadLocal下标就是5的时候,进行读取的时候就直接走缓存了,比走主存可能快2个数量级。

一点疑惑

问题:为什么这里填充了9个long值呢???

我提了一个issue:https://github.com/netty/netty/issues/9284

虽然也有人回答,但是感觉不是自己想要的,说服不了自己!!!

FastThreadLocal比ThreadLocal快,并不是空间换时间

现在清理已经去掉,本文下面会介绍,所以FastThreadLocal比ThreadLocal快,并不是空间换时间,FastThreadLocal并没有浪费空间!!!

FastThreadLocal不在使用ObjectCleaner处理泄漏,必要的时候建议重写onRemoval方法

最新的netty版本中已经不在使用ObjectCleaner处理泄漏:

https://github.com/netty/netty/commit/9b1a59df383559bc568b891d73c7cb040019aca6#diff-e0eb4e9a6ea15564e4ddd076c55978de

https://github.com/netty/netty/commit/5b1fe611a637c362a60b391079fff73b1a4ef912#diff-e0eb4e9a6ea15564e4ddd076c55978de

去掉原因:

https://github.com/netty/netty/issues/8017

我们看看FastThreadLocal的onRemoval

如果使用的是FastThreadLocalThread能保证调用的,重写onRemoval做一些收尾状态修改等等

FastThreadLocal为什么快?

FastThreadLocal操作元素的时候,使用常量下标在数组中进行定位元素来替代ThreadLocal通过哈希和哈希表,这个改动特别在频繁使用的时候,效果更加显著!计算该ThreadLocal需要存储的位置是通过hash算法确定位置:

int i = key.threadLocalHashCode & (len-1);而FastThreadLocal就是一个常量下标index,这个如果执行次数很多也是有影响的。

并且FastThreadLocal利用缓存行的特性,FastThreadLocal是通过indexedVariables数组进行存储数据的,如果有多个FastThreadLocal的时候,也可以利用缓存行,比如一次indexedVariables数组第3个位置数据,由于缓存的最小单位是缓存行,顺便把后面的4、5、6等也缓存了,下次刚刚好改线程需要读取另外的FastThreadLocal,这个FastThreadLocal的下标就是5的时候,进行读取的时候就直接走缓存了,比走主存可能快2个数量级而ThreadLocal通过hash是分散的。

转载于:https://www.cnblogs.com/CQqf2019/p/11155560.html

FastThreadLocal吞吐量居然是ThreadLocal的3倍相关推荐

  1. 惊:FastThreadLocal吞吐量居然是ThreadLocal的3倍!!!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 接着上次手撕面试题ThreadLocal!!!面试官一听,哎呦不错 ...

  2. SX1301吞吐量是SX1276/8的多少倍?

    1.吞吐量,LoRa网关的重要性能 从广义上讲,网关是连接2个不同网络的设备.如果一个设备,它能将LoRa无线网络和Internet连接起来,它就是一个LoRa网关. 目前,大部分的LoRa网关采用S ...

  3. 神马? 网飞(Netflix)程序员的基本薪资居然是谷歌的2倍多...

    众所周知: 科技公司,尤其是硅谷的科技公司, 在员工薪资上都出手很大方. 而在这些硅谷科技公司中, 又有一个公司特别突出. 它,就是Netflix. 到底有多突出呢?来感受一下: (图片来源:知乎) ...

  4. IntelliJ IDEA 18 周岁,吐血推进珍藏已久的必装插件

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 IntelliJ IDEA是目前最好最强最智能的Java IDE, ...

  5. Jenkins实践基础:使用Easypack搭建一主多从的分布式持续集成环境

    转自: https://blog.csdn.net/liumiaocn/article/details/94384235 Java面试官:给Java面试者的八点建议: https://blog.csd ...

  6. 只改了五行代码接口吞吐量提升了10多倍

    点击上方"Java基基",选择"设为星标" 做积极的人,而不是积极废人! 每天 14:00 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java ...

  7. SX1301吞吐量是SX1278的多少倍?

    SX1301吞吐量是SX1276/8的多少倍? 1 吞吐量,LoRa网关的重要性能 从广义上讲,网关是连接2个不同网络的设备.如果一个设备,它能将LoRa无线网络和Internet连接起来,它就是一个 ...

  8. 比目前世界上最快的超级计算机快 4 倍,英伟达这波玩大了!

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 来自新智元 3 月 22 日晚,备受瞩目的英伟达 GTC 开发者大会,终于开幕了! 虽 ...

  9. 什么?Python的多进程居然比单进程慢?

    导读 很多时候,当我们需要使用Python来处理大量的数据的时候,为了缩短处理的时间,我们会使用多线程或多进程来并行处理任务. 由于Python全局解释器锁的存在,导致在执行多线程的时候实际上只有一个 ...

最新文章

  1. mysql 上亿记录_一入职!就遇到上亿(MySQL)大表的优化....
  2. LeetCode Integer Replacement
  3. 条款20:为指针的关联容器指定比较类型
  4. CDOJ--1668
  5. 什么原因成就了一位优秀的程序员?(转)
  6. 4 微信公众号开发 被动回复消息 回复没有反应怎么办
  7. 用简单的代码测一测你是否真的了解:Asp.Net中的事件和委托的实现
  8. 2021上半年测试工作总结:再一次的跳出、新的转变
  9. Linux 文件umask默认权限_012
  10. 你还敢用鼠标吗?黑客在百米之外控制你的鼠标
  11. Python中 的Hash表查找与List的index()对比
  12. simulink中积分环节、惯性环节、比例环节
  13. 17AHU排位赛2 F题(bitset优化)
  14. IOS上的monkey测试探索(一)
  15. java如何控制远程桌面_Java实现屏幕抓图,控制远程桌面
  16. XMUTOJ-默罕默德的炸弹
  17. k8s常见故障排查一点通
  18. 一个springboot 项目a集成另一个springboot 项目b
  19. 利用python进行数据分析-数据聚合与分组运算2
  20. 开发c语言系统怎么分工,C语言专业课程设计工资统一标准管理系统.doc

热门文章

  1. Go 知识点(18)— 条件编译(编译标签、文件后缀)
  2. 2022-2028年中国石油钻井井下工具行业市场研究及前瞻分析报告
  3. Bert系列(三)——源码解读之Pre-train
  4. GPU版TensorFlow怎么指定让CPU运行
  5. LeetCode简单题之检查数组是否经排序和轮转得到
  6. CUDA Pro:通过向量化内存访问提高性能
  7. 超声波技术构建触摸界面
  8. Activity在有Dialog时按Home键的生命周期
  9. 客快物流大数据项目(四十九):开发环境初始化
  10. 客快物流大数据项目(九):Docker常用命令