ThreadLocal深度解析
本文基于jdk1.8.0_66写成
0. ThreadLocal简介
ThreadLocal可以提供线程内的局部对象,合理的使用可以避免线程冲突的问题
比方说SimpleDateFormat是线程不安全的,但是如果用ThreadLocal给每个线程分配一个SimpleDateFormat对象,我们就可以安全的使用了
为了便于理解,我们可以将ThreadLocal想象成一个Map,key为当前线程,value为存入的值
threadLocal.get() 等效于 map.get(Thread.currentThread())
threadLocal隐式的帮我们完成了获取当前线程的操作,使用起来更为方便
1. naive的想法
如同简介里所说,ThreadLocal最直接的设计思路是:
在ThreadLocal内部维护一个Map,key为当前线程,value为存入的值
ThreadLocal.set(obj)等效于Map.set(Thread.currentThread(), obj)
ThreadLocal.get()等效于Map.get(Thread.currentThread())
这个想法的优点是实现简单,但是问题也很多:
a. 一般来说,一个ThreadLocal会与多个Thread关联,而一个Thread只会与少数的ThreadLocal关联。所以从ThreadLocal去寻找关联的Thread,开销比从Thread寻找关联的ThreadLocal要大。
b. 如果一个Thread死掉了,为了防止内存泄漏,所有ThreadLocal中与这个Thread关联的value都要被释放,这个过程是手动的,不优雅,而且开销较大。
2. JDK1.8中的想法
每个Thread各自维护一个名为threadLocals的变量,其类型为ThreadLocal.ThreadLocalMap
这个Map的key是ThreadLocal,value是关联的值
在调用ThreadLocal.get/set时,先用Thread.currentThread()获得当前Thread,然后找到当前Thread的threadLocals域,再以当前ThreadLocal为key找到对应的value并返回。
这样做好处很多:
a. 一般来说,一个Thread只会与少数的几个ThreadLocal关联,那么从Thread去寻找对应的ThreadLocal开销是很小的
b. 如果一个Thread死掉了,那么它所关联的threadLocals也会被自动释放,在很大程度上避免了内存泄漏的问题
3. Netty中的进一步改进
Netty自定义了一个名为FastThreadLocal的东西
大概想法:
原版ThreadLocal中,在ThreadLocal.ThreadLocalMap中查找时,采用的是线性探测法,发生哈希碰撞时会导致查询变慢
为了避免这一问题,Netty为每个FastThreadLocal都设置了独一无二的编号,Thread可以直接根据这个编号寻址
这样做绝对不会有哈希碰撞,但是占用的空间也相应变大了,也就是空间换时间的套路。
4. ThreadLocal与内存泄漏
ThreadLocal有个很微妙的地方在于,它在某些场景下,还是会发生内存泄漏
如果我们在函数里定义了一个局部的ThreadLocal变量,主线程往里面set了一个很大的对象Huge后就退出这个函数该干嘛干嘛去了
现在这个ThreadLocal连带着Huge都是垃圾了,但是gc能回收他们吗?
按照一般的思路,ThreadLocal和Huge都会被Thread自带的threadLocals引用,所以都不会被回收。
但是JDK的作者比我机智很多了,他们把ThreadLocal.ThreadLocalMap.Entry弄成了弱引用(WeakReference),也就是说没有引用的ThreadLocal对象是会在full gc中被回收的。
但是问题依然存在:虽然ThreadLocal被回收了,但是它关联的Huge对象却还在,这可如何是好?
JDK的作者此时又玩了个骚操作,在对ThreadLocal做set操作时,会去检查ThreadLocal.ThreadLocalMap的底层数组,如果发现某个key是null了(ThreadLocal被gc了),它会把对应的value也设为null,这样Huge对象就可以被释放了。
但是为了性能考虑,这个检查操作不会遍历整个底层数组,而是每次只扫描一小段,所以在某些特定的场景下,还是会发生内存泄漏的。
ThreadLocal深度解析的更多相关文章
- ThreadLocal深度解析和应用示例
开篇明意 ThreadLocal是JDK包提供的线程本地变量,如果创建了ThreadLocal<T>变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的 ...
- Java ThreadLocal深度解析
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各 ...
- mybatis 3.x源码深度解析与最佳实践(最完整原创)
mybatis 3.x源码深度解析与最佳实践 1 环境准备 1.1 mybatis介绍以及框架源码的学习目标 1.2 本系列源码解析的方式 1.3 环境搭建 1.4 从Hello World开始 2 ...
- Spring源码深度解析之事务
Spring源码深度解析之事务 目录 一.JDBC方式下的事务使用示例 (1)创建数据表结构 (2)创建对应数据表的PO (3)创建表和实体之间的映射 (4)创建数据操作接口 (5)创建数据操作接口实 ...
- [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析
[WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析 标签: webkit内核JavaScriptCore 2015-03-26 23:26 2285 ...
- 第37课 深度解析QMap与QHash
1. QMap深度解析 (1)QMap是一个以升序键顺序存储键值对的数据结构 ①QMap原型为 class QMap<K, T>模板 ②QMap中的键值对根据Key进行了排序 ③QMap中 ...
- Deep Learning模型之:CNN卷积神经网络(一)深度解析CNN
http://m.blog.csdn.net/blog/wu010555688/24487301 本文整理了网上几位大牛的博客,详细地讲解了CNN的基础结构与核心思想,欢迎交流. [1]Deep le ...
- (转载)(收藏)OceanBase深度解析
一.OceanBase不需要高可靠服务器和高端存储 OceanBase是关系型数据库,包含内核+OceanBase云平台(OCP).与传统关系型数据库相比,最大的不同点, 是OceanBase是分布式 ...
- Kafka深度解析
本文转发自Jason’s Blog,原文链接 http://www.jasongj.com/2015/01/02/Kafka深度解析 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅 ...
随机推荐
- 段寻址*****************************TBD
fffff880`01b05be1 ff9708020000 call qword ptr [rdi+208h] ds:002b:fffff980`0554ae88=fffffa8004b ...
- Aspose.words 替换字符 操作
var path = Server.MapPath("~/doc/demo.doc"); Document doc = new Document(path); DocumentBu ...
- [剑指Offer] 41.和为S的连续正数序列
题目描述 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数).没多久,他 ...
- Dubbo 项目与传统项目
1.什么是传统工程 单工程 MVC 架构 控制层通过调用服务层完成业务逻辑处理 业务层调用持久层进程数据操作 2.什么是分布式工程 将传统项目的单工程结构,拆分成多工程 一般会有这几个工程: 父工程: ...
- 对C++ templates类模板的几点补充(Traits类模板特化)
前一篇文章<浅谈C++ templates 函数模板.类模板以及非类型模板参数>简单的介绍了什么是函数模板(这个最简单),类模板以及非类型模板参数.本文对类模板再做几点补充. 文章目录1. ...
- “CNKI 中国知网 PDF 全文下载”油猴脚本在线安装地址
https://greasyfork.org/zh-CN/scripts/18841-cnki-%E4%B8%AD%E5%9B%BD%E7%9F%A5%E7%BD%91-pdf-%E5%85%A8%E ...
- 第116讲 boost::algorithm::string之替换和删除
http://www.360doc.com/content/16/0523/18/29304643_561672752.shtml
- tomcat 配置文件中设置JAVA_HOME
Tomcat默认情况下会用系统的环境变量中找到JAVA_HOME和JRE_HOME.但是有的时候我们需要不同版本的JDK共存. 可以在${TOMCAT_HOME}/bin/setclasspath.b ...
- Jquery 获取checkbox的checked问题以及解决方案
转载自:http://www.cnblogs.com/-run/archive/2011/11/16/2251250.html 这个郁闷了,今天写这个功能的时候发现了问题,上网找了好多资料对照,更加纠 ...
- c++ 公有继承的赋值兼容规则
赋值兼容规则是指在需要基类对象的任何地方都可以使用公有派生类的对象来替代.通过公有继承,派生类得到了基类中除构造函数.析构函数之外的所有成员,而且所有成员的访问控制属性也和基类完全相同.这样,公有派生 ...