ThreadLocal源码实现。
今天在FileInputStream源码中看到有ThreadLocal,之前一直没有理解过这个类,现在进行补充。

ThreadLocal即为线程局部变量,它和同步机制处理的是不同的问题域,同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信;ThreadLocal是隔离多个线程的数据共享,根本上就没有多线程对资源的共享。所以如果需要多线程之间进行通信,那么需要同步机制;如果需要隔离多线程之间的资源共享冲突,就需要ThreadLocal。因此,ThreadLocal的使用场景是按照线程来单独使用某个对象,即每个线程都使用一个对象(变量),但是线程之间是隔离的,每个对象在相应的线程中都是独立的状态,与其他线程中的状态无关,相当于线程的专属变量。
比如有两个线程A和B,有一个资源或者变量apple,apple的初始值10,同步机制是这样的,当A.eatApple()后,apple的数量为9,B去eat的时候apple的数量就是9了;但是对于ThreadLocal来说,B去eat的时候apple的数量还是10,只有对A线程来说apple的数量是9,这也便是线程局部变量的意思。
- protected T initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
protected T initialValue() {
return null;
}
- void set(Object value)设置当前线程的线程局部变量的值。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
- public T get()该方法返回当前线程所对应的线程局部变量。
- public T get() {
- Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
- public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Spring的事务管理器中,对数据源获取的Connection放入了ThreadLocal中,程序执行完后由ThreadLocal中获取connection然后做commit和rollback,使用中,要保证程序通过DataSource获取的connection就是从spring中获取的,为什么要做这样的操作呢,因为业务代码完全由应用程序来决定,而框架不能要求业务代码如何去编写,否则就失去了框架不让业务代码去管理connection的好处了,此时业务代码被切入后,spring不会向业务代码区传入一个connection,它必须保存在一个地方,当底层通过ibatis、spring jdbc等框架获取同一个datasource的connection的时候,就会调用按照spring约定的规则去获取,由于执行过程都是在同一个线程中处理,从而获取到相同的connection,以保证commit、rollback以及业务操作过程中,使用的connection是同一个,因为只有同一个conneciton才能保证事务,否则数据库本身也是不支持的。
public class ConnectionUtil {
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
private static Connection initConn = null;
static {
try {
initConn = DriverManager.getConnection("url, name and password");
} catch (SQLException e) {
e.printStackTrace();
}
}
public Connection getConn() {
Connection c = tl.get();
if(null == c) tl.set(initConn);
return tl.get();
}
}
1、Thread里面有个属性是一个类似于HashMap一样的东西,只是它的名字叫ThreadLocalMap,这个属性是default类型的,因此同一个package下面所有的类都可以引用到,因为是Thread的局部变量,所以每个线程都有一个自己单独的Map,相互之间是不冲突的,所以即使将ThreadLocal定义为static线程之间也不会冲突。
2、ThreadLocal和Thread是在同一个package下面,可以引用到这个类,可以对他做操作,此时ThreadLocal每定义一个,用this作为Key,你传入的值作为value,而this就是你定义的ThreadLocal,所以不同的ThreadLocal变量,都使用set,相互之间的数据不会冲突,因为他们的Key是不同的,当然同一个ThreadLocal做两次set操作后,会以最后一次为准。
3、综上所述,在线程之间并行,ThreadLocal可以像局部变量一样使用,且线程安全,且不同的ThreadLocal变量之间的数据毫无冲突。
这个ThreadLocal有啥坑呢,大家从前面应该可以看出来,这个ThreadLocal相关的对象是被绑定到一个Map中的,而这个Map是Thread线程的中的一个属性,那么就有一个问题是,如果你不自己remove的话或者说如果你自己的程序中不知道什么时候去remove的话,那么线程不注销,这些被set进去的数据也不会被注销。
反过来说,写代码中除非你清晰的认识到这个对象应该在哪里set,哪里remove,如果是模糊的,很可能你的代码中不会走remove的位置去,或导致一些逻辑问题,另外,如果不remove的话,就要等线程注销,我们在很多应用服务器中,线程是被复用的,因为在内核分配线程还是有开销的,因此在这些应用中线程很难会被注销掉,那么向ThreadLocal写入的数据自然很不容易被注销掉,这些可能在我们使用某些开源框架的时候无意中被隐藏用到,都有可能会导致问题,最后发现OOM得时候数据竟然来自ThreadLocalMap中,还不知道这些数据是从哪里设置进去的,所以你应当注意这个坑,可能不止一个人掉进这个坑里去过
下面来看一个hibernate中典型的ThreadLocal的应用:
- private static final ThreadLocal threadSession = new ThreadLocal();
- public static Session getSession() throws InfrastructureException {
- Session s = (Session) threadSession.get();
- try {
- if (s == null) {
- s = getSessionFactory().openSession();
- threadSession.set(s);
- }
- } catch (HibernateException ex) {
- throw new InfrastructureException(ex);
- }
- return s;
- }
可以看到在getSession()方法中,首先判断当前线程中有没有放进去session,如果还没有,那么通过sessionFactory().openSession()来创建一个session,再将session set到线程中,实际是放到当前线程的ThreadLocalMap这个map中,这时,对于这个session的唯一引用就是当前线程中的那个ThreadLocalMap(下面会讲到),而threadSession作为这个值的key,要取得这个session可以通过threadSession.get()来得到,里面执行的操作实际是先取得当前线程中的ThreadLocalMap,然后将threadSession作为key将对应的值取出。这个session相当于线程的私有变量,而不是public的。
显然,其他线程中是取不到这个session的,他们也只能取到自己的ThreadLocalMap中的东西。要是session是多个线程共享使用的,那还不乱套了。
试想如果不用ThreadLocal怎么来实现呢?可能就要在action中创建session,然后把session一个个传到service和dao中,这可够麻烦的。或者可以自己定义一个静态的map,将当前thread作为key,创建的session作为值,put到map中,应该也行,这也是一般人的想法,但事实上,ThreadLocal的实现刚好相反,它是在每个线程中有一个map,而将ThreadLocal实例作为key,这样每个map中的项数很少,而且当线程销毁时相应的东西也一起销毁了,不知道除了这些还有什么其他的好处。
参考: http://www.importnew.com/18077.html
ThreadLocal源码实现。的更多相关文章
- Java多线程9:ThreadLocal源码剖析
ThreadLocal源码剖析 ThreadLocal其实比较简单,因为类里就三个public方法:set(T value).get().remove().先剖析源码清楚地知道ThreadLocal是 ...
- Java多线程学习之ThreadLocal源码分析
0.概述 ThreadLocal,即线程本地变量,是一个以ThreadLocal对象为键.任意对象为值的存储结构.它可以将变量绑定到特定的线程上,使每个线程都拥有改变量的一个拷贝,各线程相同变量间互不 ...
- Java并发编程之ThreadLocal源码分析
## 1 一句话概括ThreadLocal<font face="微软雅黑" size=4> 什么是ThreadLocal?顾名思义:线程本地变量,它为每个使用该对象 ...
- ThreadLocal源码解读
1. 背景 ThreadLocal源码解读,网上面早已经泛滥了,大多比较浅,甚至有的连基本原理都说的很有问题,包括百度搜索出来的第一篇高访问量博文,说ThreadLocal内部有个map,键为线程对象 ...
- 并发编程(四)—— ThreadLocal源码分析及内存泄露预防
今天我们一起探讨下ThreadLocal的实现原理和源码分析.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用需要注意的地方,最后给出了两 ...
- ThreadLocal详解,ThreadLocal源码分析,ThreadLocal图解
本文脉路: 概念阐释 ----> 原理图解 ------> 源码分析 ------> 思路整理 ----> 其他补充. 一.概念阐述. ThreadLocal 是一个为 ...
- 【JAVA】ThreadLocal源码分析
ThreadLocal内部是用一张哈希表来存储: static class ThreadLocalMap { static class Entry extends WeakReference<T ...
- java多线程17:ThreadLocal源码剖析
ThreadLocal源码剖析 ThreadLocal其实比较简单,因为类里就三个public方法:set(T value).get().remove().先剖析源码清楚地知道ThreadLocal是 ...
- 并发-ThreadLocal源码分析
ThreadLocal源码分析 参考: http://www.cnblogs.com/dolphin0520/p/3920407.html https://www.cnblogs.com/coshah ...
- 2018/3/3 解析ThreadLocal源码
今天听到一个老哥说道ThreadLocal在源码设计上面的一些好处,于是决定把ThreadLocal源码彻底分析一下. 首先,我们来看下set方法 可以看到,这个方法里,先获得了当前线程,之后将当前线 ...
随机推荐
- Mybatis 不同使用方式
前言 工作这么多年,ORM框架一直选择Mybatis框架. Mybatis的使用方式也一直在变,总体来说是越来越简单.写篇文章对各使用方式做个总结... 正文 一.Mybatis典型用法 1. 正常执 ...
- class="no-js"
这是什么意思?看了外网的解释,比较明白了.(When Modernizr runs, it removes the "no-js" class and replaces it wi ...
- 使用FFmpeg常见问题
使用FFmpeg常见问题 https://blog.csdn.net/willib/article/details/52530328 https://blog.csdn.net/nogodoss/ar ...
- 网站性能优化实战——从12.67s到1.06s的故事
文章摘自https://juejin.im/post/5b0b7d74518825158e173a0c 作为互联网项目,最重要的便是用户体验.在举国“互联网+”的热潮中,用户至上也已经被大多数企业所接 ...
- iOS之分类(category)
1.分类(category)的作用 1.1作用:可以在不修改原来类的基础上,为一个类扩展方法.1.2最主要的用法:给系统自带的类扩展方法. 2.分类中能写点啥? 2.1分类中只能添加“方法”,不能增加 ...
- Flink - watermark生成
参考,Flink - Generating Timestamps / Watermarks watermark,只有在有window的情况下才用到,所以在window operator前加上assig ...
- oplog
参考资料:https://www.cnblogs.com/ruizhang3/p/6539730.html http://www.jb51.net/article/113432.htm :insert ...
- Linux and Oracle常用目录详解
目录详解 目录 内容 / 根目录,一切从这里开始 /bin 包含系统启动和运行所必需的二进制文件(程序) /boot 包含Linux内核.最初的RAM磁盘映像(系统启动时,驱动程序会用到),以及启动加 ...
- python time模块总结
常用函数: import time --------------------------------------------------time.time() ...
- 洛谷P3247 最小公倍数 [HNOI2016] 分块+并查集
正解:分块+并查集 解题报告: 传送门! 真的好神仙昂QAQ,,,完全想不出来,,,还是太菜了QAQ 首先还是要说下,这题可以用K-D Tree乱搞过去(数据结构是个好东西昂,,,要多学学QAQ),但 ...