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的更多相关文章

  1. 【Netty源码学习】DefaultChannelPipeline(三)

    上一篇博客中[Netty源码学习]ChannelPipeline(二)我们介绍了接口ChannelPipeline的提供的方法,接下来我们分析一下其实现类DefaultChannelPipeline具 ...

  2. 【Netty源码学习】ChannelPipeline(一)

    ChannelPipeline类似于一个管道,管道中存放的是一系列对读取数据进行业务操作的ChannelHandler. 1.ChannelPipeline的结构图: 在之前的博客[Netty源码学习 ...

  3. 【Netty源码学习】ServerBootStrap

    上一篇博客[Netty源码学习]BootStrap中我们介绍了客户端使用的启动服务,接下来我们介绍一下服务端使用的启动服务. 总体来说ServerBootStrap有两个主要功能: (1)调用父类Ab ...

  4. Netty 源码学习——EventLoop

    Netty 源码学习--EventLoop 在前面 Netty 源码学习--客户端流程分析中我们已经知道了一个 EventLoop 大概的流程,这一章我们来详细的看一看. NioEventLoopGr ...

  5. Netty 源码学习——客户端流程分析

    Netty 源码学习--客户端流程分析 友情提醒: 需要观看者具备一些 NIO 的知识,否则看起来有的地方可能会不明白. 使用版本依赖 <dependency> <groupId&g ...

  6. Netty源码学习系列之4-ServerBootstrap的bind方法

    前言 今天研究ServerBootstrap的bind方法,该方法可以说是netty的重中之重.核心中的核心.前两节的NioEventLoopGroup和ServerBootstrap的初始化就是为b ...

  7. 【Netty源码学习】EventLoopGroup

    在上一篇博客[Netty源码解析]入门示例中我们介绍了一个Netty入门的示例代码,接下来的博客我们会分析一下整个demo工程运行过程的运行机制. 无论在Netty应用的客户端还是服务端都首先会初始化 ...

  8. (一)Netty源码学习笔记之概念解读

    尊重原创,转载注明出处,原文地址:http://www.cnblogs.com/cishengchongyan/p/6121065.html  博主最近在做网络相关的项目,因此有契机学习netty,先 ...

  9. netty源码学习

    概述 Netty is an asynchronous event-driven network application framework for rapid development of main ...

随机推荐

  1. Windows下使用Nginx+tomcat配置负载均衡

    Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行.由俄罗斯的程序设计师Igor Sysoev所开发,供俄国大型的入口 ...

  2. 《Cracking the Coding Interview》——第13章:C和C++——题目4

    2014-04-25 19:50 题目:深拷贝和浅拷贝有什么区别?如何应用? 解法:深拷贝传值,浅拷贝传引用.java里对此做了限制,而C++里面用起来更自由.大结构不宜传值,因为拷贝过程效率低. 代 ...

  3. 《Cracking the Coding Interview》——第4章:树和图——题目8

    2014-03-19 05:04 题目:给定两棵二叉树T1和T2,判断T2是否是T1的子树.子树的定义是,以T1的某个节点(可以是T1的根)作为根节点,得到的这棵树和T2一模一样. 解法:首先可以根据 ...

  4. 《Cracking the Coding Interview》——第4章:树和图——题目5

    2014-03-19 04:11 题目:设计算法检查一棵二叉树是否为二叉搜索树. 解法:既然是二叉搜索树,也就是说左子树所有节点都小于根,右子树所有节点都大于根.如果你真的全都检查的话,那就做了很多重 ...

  5. java程序员笑不死的经历ส้้้้้้้้้

    ส้้้้้้้้้้ส้้้้้้้้้้ส้้้้้้้้้ 1.程序猿最烦两件事,第一件事是别人要求他给自己的代码写文档,第二件呢?是别人的程序没有留下文档. 2.宪法顶个球!中国的法律都是.t ...

  6. windows系统设备管理器显示全部硬件

    下面的小命令能让隐藏的未卸载掉的硬件设备彻底现身:开始-运行-CMD C:\> C:\>start devmgmt.msc 之后再在Windows 的设备管理器中,单击菜单“显示”-“显示 ...

  7. 【bzoj2770】YY的Treap 权值线段树

    题目描述 志向远大的YY小朋友在学完快速排序之后决定学习平衡树,左思右想再加上SY的教唆,YY决定学习Treap.友爱教教父SY如砍瓜切菜般教会了YY小朋友Treap(一种平衡树,通过对每个节点随机分 ...

  8. [poj] 3907 Build Your Home || 求多边形面积

    原题 多组数据,到0为止. 每次给出按顺序的n个点(可能逆时针,可能顺时针),求多边形面积(保留整数) 多边形面积为依次每条边(向量)叉积/2的和 \(S=\sum _{i=1}^{n-1}p[i]* ...

  9. ZOJ Bizarre Routine 2013杭州网赛B题

    题目意思: 给定n, expect, a, b 要求你构造一组array[],存放一个1..n的排列,使的下面的程序能输出YES 题目所示代码: bool less_than(x, y) { T++; ...

  10. 太空飞船(spaceship)

    太空飞船(spaceship) 题目描述 21XX年,秋. 小诚是THU(Tomorrow Happy University)航天学院船舶设计系本科四年级的学生.为了顺利毕业,小诚仔细阅读了这几年被引 ...