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 ...
随机推荐
- USACO Section1.5 Prime Palindromes 解题报告
pprime解题报告 —— icedream61 博客园(转载请注明出处)--------------------------------------------------------------- ...
- python3知识点之---------字符串的介绍
1. 定义 其实字符串就是一系列字符,用引号括起来的就是字符串,其中的引号可以是单引号或者双引号. 比如 "This is a string" 'This is a strin ...
- lo口环路问题分析
流程如下,collecter抓取网卡lo和wlan0数据,其中lo口无数据,wlan0是笔记本上网网口,然后按自定义协议把数据包通过lo口发给后端dispatch进行分发! 这种模式下,抓包程序每经过 ...
- android自定义SlideMenu源码详解之最简单侧滑实现
实现原理:在一个Activity的布局中需要有两部分,一个是菜单(menu)的布局,一个是内容(content)的布局.两个布局横向排列,菜单布局在左,内容布局在右.初始化的时候将菜单布局向左偏移,以 ...
- poj 3080 kmp求解多个字符串的最长公共字串,(数据小,有点小暴力 16ms)
Blue Jeans Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 14113 Accepted: 6260 Descr ...
- gulp入门1
1. 下载.安装git(https://git-scm.com/downloads),学会使用命令行. 2. 下载.安装node.js(https://nodejs.org/en/),现在node.j ...
- [洛谷P3509][POI2010]ZAB-Frog
题目大意:有$n$个点,每个点有一个距离(从小到大给出),从第$i$个点跳一次,会跳到距离第$i$个点第$k$远的点上(若有两个点都是第$k$远,就跳到编号小的上).问对于从每个点开始跳,跳$m$次, ...
- POJ 1273 Drainage Ditches | 最大流模板
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> #defi ...
- 2017 多校5 Rikka with String
2017 多校5 Rikka with String(ac自动机+dp) 题意: Yuta has \(n\) \(01\) strings \(s_i\), and he wants to know ...
- PL/SQL 查询结果集直接修改数据
使用t.rowid,查询可以直接在查询结果中修改提交 SELECT t.rowid,t.* from UC_ROLE t where ROLE_NAME like '% %'