Netty源码学习(七)FastThreadLocal
0. FastThreadLocal简介
如同注释中所说:A special variant of ThreadLocal that yields higher access performance when accessed from a FastThreadLocalThread.
这是ThreadLocal的变种,但是有更高的性能
ps.本文涉及的源码版本如下:
JDK : java-1.8.0-openjdk-1.8.0.141-1.b16.ojdkbuild.windows.x86_64
Netty : 4.1.15.Final
1. JDK自带的ThreadLocal的工作原理,以及存在的问题
原理简介:
a. 每个线程内部维护了一个ThreadLocal.ThreadLocalMap类型的变量threadLocals
b. ThreadLocalMap是由数组实现的Map,key为ThreadLocal,value为对应的变量
c. 对ThreadLocal进行get/set操作时,会先获取当前Thread内部的ThreadLocal.ThreadLocalMap,然后以ThreadLocal为key,从这个Map中获取对应的value就是结果
设计理念:
a. ThreadLocal中的数据实际存放于Thread中,线程死亡时,这些数据会被自动释放,减小了开销
b. 一般来说,一个ThreadLocal对应的Thread数量远多于一个Thread所对应的ThreadLocal数量,因此Thead内部维护的ThreadLocal.ThreadLocalMap的长度一般来说是较短的,寻址快速
存在的问题:
直接跟踪ThreadLocal.get()方法的调用链:
ThreadLocalMap.get()
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//获取当前线程内部维护的ThreadLocalMap对象
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//以当前TheadLocal为key,在ThreadLocalMap中查询数据
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
} ThreadLocal.ThreadLocalMap.getEntry()
/**
* Get the entry associated with key. This method
* itself handles only the fast path: a direct hit of existing
* key. It otherwise relays to getEntryAfterMiss. This is
* designed to maximize performance for direct hits, in part
* by making this method readily inlinable.
*
* @param key the thread local object
* @return the entry associated with key, or null if no such
*/
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);//ThreadLocal的threadLocalHashCode是在定义ThreadLocal时产生的一个伪随机数,可以理解为ThreadLocal的hashCode,此处用其计算ThreadLocal在ThreadLocalMap中的下标
Entry e = table[i];//寻址
if (e != null && e.get() == key)//命中
return e;
else
return getEntryAfterMiss(key, i, e);//未命中,目标地址上存储了另外一个ThreadLocal及其对应的value(hash碰撞)
} ThreadLocal.ThreadLocalMap.getEntryAfterMiss()
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length; while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);//如果key为null,则删除对应的value(由于ThreadLocalMap中的Entry扩展于WeakReference,因此如果ThreadLocal没有强引用的情况下,ThreadLocal会被gc回收掉,此时key为空。为了便于gc,需要同时删除对value的引用)
else
i = nextIndex(i, len);//查找ThreadLocalMap中的下一个元素,直到命中为止(很明确的线性探测法)
e = tab[i];
}
return null;
}
代码逻辑不算复杂,其问题在于在ThreadLocal.ThreadLocalMap中查找时,采用的是线性探测法,一般情况下时间复杂度是O(1),但是在发生哈希冲突时,可能会退化到O(n)的时间复杂度。
Netty中针对此处做出了下面的优化
2. Netty中的FastThreadLocal原理
原理简介:
a. FastThreadLocal的构造方法中,会为当前FastThreadLocal分配一个index,这个index是由一个全局唯一的static类型的AtomInteger产生的,可以保证每个FastThreadLocal的index都不同
b. FastThreadLocal需要与FastThreadLocalThread配套使用(FastThreadLocalThread内部维护了一个InternalThreadLocalMap类型的threadLocalMap属性,在调用FastThreadLocal的get方法时会去这个InternalThreadLocalMap中查询)
c. InternalThreadLocalMap内部也是使用数组作为底层存储,key为FastThreadLocal,寻址方式是直接使用FastThreadLocal内部维护的index,由于每个FastThreadLocal的index都不同,因此不会发生hash冲突,直接取数据即可,效率极高
d. 代价是有多少个FastThreadLocal对象, InternalThreadLocalMap内部就得开多大的底层数组(为了防止频繁扩容,实际上还要略大一点),也就是空间换时间了
源码分析:
//FastThreadLocal的构造方法中创建全局唯一的index的过程
public FastThreadLocal() {
index = InternalThreadLocalMap.nextVariableIndex();
} static final AtomicInteger nextIndex = new AtomicInteger();
public static int nextVariableIndex() {
int index = nextIndex.getAndIncrement();
if (index < 0) {
nextIndex.decrementAndGet();
throw new IllegalStateException("too many thread-local indexed variables");
}
return index;
} FastThreadLocal.get()
public final V get() {
return get(InternalThreadLocalMap.get());
} InternalThreadLocalMap.get()//先从当前线程中获取维护的InternalThreadLocalMap属性
public static InternalThreadLocalMap get() {
Thread thread = Thread.currentThread();
if (thread instanceof FastThreadLocalThread) {
return fastGet((FastThreadLocalThread) thread);
} else {
return slowGet();
}
} InternalThreadLocalMap.fastGet()
private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
if (threadLocalMap == null) {
thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
}
return threadLocalMap;
} FastThreadLocal.get()
public final V get(InternalThreadLocalMap threadLocalMap) {
Object v = threadLocalMap.indexedVariable(index);
if (v != InternalThreadLocalMap.UNSET) {
return (V) v;
} return initialize(threadLocalMap);
} public Object indexedVariable(int index) {
Object[] lookup = indexedVariables;//indexedVariables就是InternalThreadLocalMap的底层数组了
return index < lookup.length? lookup[index] : UNSET;
}
逻辑还是比较清晰的,我就不再做解释了
3. 总结
Netty的FastThreadLocal与JDK自带的ThreadLocal的设计理念实际上还是相通的,都是在Thread的内部维护了一个Map,然后把数据记录在其中,只是Netty摒弃了JDK中采用的线性探测法,而是为每个FastThreadLocal对象生成一个全局唯一的index,并以此在Map中寻址,这样就直接解决了哈希冲突的问题。至于高性能与空间浪费的取舍,就见仁见智了。
Netty源码学习(七)FastThreadLocal的更多相关文章
- 【Netty源码学习】DefaultChannelPipeline(三)
上一篇博客中[Netty源码学习]ChannelPipeline(二)我们介绍了接口ChannelPipeline的提供的方法,接下来我们分析一下其实现类DefaultChannelPipeline具 ...
- 【Netty源码学习】ChannelPipeline(一)
ChannelPipeline类似于一个管道,管道中存放的是一系列对读取数据进行业务操作的ChannelHandler. 1.ChannelPipeline的结构图: 在之前的博客[Netty源码学习 ...
- 【Netty源码学习】ServerBootStrap
上一篇博客[Netty源码学习]BootStrap中我们介绍了客户端使用的启动服务,接下来我们介绍一下服务端使用的启动服务. 总体来说ServerBootStrap有两个主要功能: (1)调用父类Ab ...
- Netty 源码学习——EventLoop
Netty 源码学习--EventLoop 在前面 Netty 源码学习--客户端流程分析中我们已经知道了一个 EventLoop 大概的流程,这一章我们来详细的看一看. NioEventLoopGr ...
- Netty 源码学习——客户端流程分析
Netty 源码学习--客户端流程分析 友情提醒: 需要观看者具备一些 NIO 的知识,否则看起来有的地方可能会不明白. 使用版本依赖 <dependency> <groupId&g ...
- Netty源码学习系列之4-ServerBootstrap的bind方法
前言 今天研究ServerBootstrap的bind方法,该方法可以说是netty的重中之重.核心中的核心.前两节的NioEventLoopGroup和ServerBootstrap的初始化就是为b ...
- 【Netty源码学习】EventLoopGroup
在上一篇博客[Netty源码解析]入门示例中我们介绍了一个Netty入门的示例代码,接下来的博客我们会分析一下整个demo工程运行过程的运行机制. 无论在Netty应用的客户端还是服务端都首先会初始化 ...
- (一)Netty源码学习笔记之概念解读
尊重原创,转载注明出处,原文地址:http://www.cnblogs.com/cishengchongyan/p/6121065.html 博主最近在做网络相关的项目,因此有契机学习netty,先 ...
- netty源码学习
概述 Netty is an asynchronous event-driven network application framework for rapid development of main ...
随机推荐
- Win Server 8中的利器:微软在线备份服务
微软在Windows Server 8中添加在线备份服务了?你一定以为我在开玩笑,是吧?但是微软确实这么做了. 微软在Windows Server 8中添加在线备份服务了?你一定以为我在开玩笑 ...
- CSS系列(5)-如何使用Firebug查看网页的html和css
Firebug是火狐浏览器Firefox的一个插件,专门为开发人员开发的.使用Firebug需要先在Firefox中安装这个插件,网上有很多教程,可以对照着安装一下. 不同的火狐浏览器版本中的Fire ...
- 关于ADB push 出现failed to copy 'D:\file.xtxt' to '/system/temp/' : Read-only file system 的报错信息解决办法
首先使用USB连接电脑与小机,然后安装adb相应的驱动,这是第一步,也是必须要做的. 进入doc系统后,敲入adb shell 可以进入linux命令行状态,说明adb可以使用了. 回到标题,我们现 ...
- 算法のLowLow三人行
点击
- Python学习-day20 django进阶篇
Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接数据库,并编写数据访问层代码 业务逻辑层去调用数据访问层执行 ...
- [python] 网络数据采集 操作清单 BeautifulSoup、Selenium、Tesseract、CSV等
Python网络数据采集操作清单 BeautifulSoup.Selenium.Tesseract.CSV等 Python网络数据采集操作清单 BeautifulSoup.Selenium.Tesse ...
- springboot08 jdbc
一.JDBC&连接池 1. jdbc介绍 JDBC(Java DataBase Connectivity ,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数 ...
- 史林枫:sqlserver数据库中数据日志的压缩及sqlserver占用内存管理设置
使用sqlserver和IIS开发.net B/S程序时,数据量逐渐增多,用户也逐渐增多,那么服务器的稳定性就需要维护了.数据库如何占用更小内存,无用的日志如何瞬间清空? 今天在给一个客户维护网站的时 ...
- 阻塞&&非阻塞
读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回.但是从终端设备或网络读则不一定,如果从终端输入的数据没有换行符,调用read读终端设备就会阻塞,如果网络上没有接收到数据包,调 ...
- 防御暴力破解SSH攻击
托管在IDC的机器我们通常都用SSH方式来远程管理.但是经常可以发现log-watch的日志中有大量试探登录的 信息,为了我们的主机安全,有必要想个方法来阻挡这些可恨的"HACKER&quo ...