今天在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的应用:

  1. private static final ThreadLocal threadSession = new ThreadLocal();
  2. public static Session getSession() throws InfrastructureException {
  3. Session s = (Session) threadSession.get();
  4. try {
  5. if (s == null) {
  6. s = getSessionFactory().openSession();
  7. threadSession.set(s);
  8. }
  9. } catch (HibernateException ex) {
  10. throw new InfrastructureException(ex);
  11. }
  12. return s;
  13. }

可以看到在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源码实现。的更多相关文章

  1. Java多线程9:ThreadLocal源码剖析

    ThreadLocal源码剖析 ThreadLocal其实比较简单,因为类里就三个public方法:set(T value).get().remove().先剖析源码清楚地知道ThreadLocal是 ...

  2. Java多线程学习之ThreadLocal源码分析

    0.概述 ThreadLocal,即线程本地变量,是一个以ThreadLocal对象为键.任意对象为值的存储结构.它可以将变量绑定到特定的线程上,使每个线程都拥有改变量的一个拷贝,各线程相同变量间互不 ...

  3. Java并发编程之ThreadLocal源码分析

    ## 1 一句话概括ThreadLocal<font face="微软雅黑" size=4>  什么是ThreadLocal?顾名思义:线程本地变量,它为每个使用该对象 ...

  4. ThreadLocal源码解读

    1. 背景 ThreadLocal源码解读,网上面早已经泛滥了,大多比较浅,甚至有的连基本原理都说的很有问题,包括百度搜索出来的第一篇高访问量博文,说ThreadLocal内部有个map,键为线程对象 ...

  5. 并发编程(四)—— ThreadLocal源码分析及内存泄露预防

    今天我们一起探讨下ThreadLocal的实现原理和源码分析.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用需要注意的地方,最后给出了两 ...

  6. ThreadLocal详解,ThreadLocal源码分析,ThreadLocal图解

    本文脉路: 概念阐释 ---->  原理图解  ------> 源码分析 ------>  思路整理  ----> 其他补充. 一.概念阐述. ThreadLocal 是一个为 ...

  7. 【JAVA】ThreadLocal源码分析

    ThreadLocal内部是用一张哈希表来存储: static class ThreadLocalMap { static class Entry extends WeakReference<T ...

  8. java多线程17:ThreadLocal源码剖析

    ThreadLocal源码剖析 ThreadLocal其实比较简单,因为类里就三个public方法:set(T value).get().remove().先剖析源码清楚地知道ThreadLocal是 ...

  9. 并发-ThreadLocal源码分析

    ThreadLocal源码分析 参考: http://www.cnblogs.com/dolphin0520/p/3920407.html https://www.cnblogs.com/coshah ...

  10. 2018/3/3 解析ThreadLocal源码

    今天听到一个老哥说道ThreadLocal在源码设计上面的一些好处,于是决定把ThreadLocal源码彻底分析一下. 首先,我们来看下set方法 可以看到,这个方法里,先获得了当前线程,之后将当前线 ...

随机推荐

  1. [No0000EB]C# 数组(Array)

    数组是一个存储相同类型元素的固定大小的顺序集合.数组是用来存储数据的集合,通常认为数组是一个同一类型变量的集合. 声明数组变量并不是声明 number0.number1.....number99 一个 ...

  2. java高级精讲之高并发抢红包~揭开Redis分布式集群与Lua神秘面纱

    java高级精讲之高并发抢红包~揭开Redis分布式集群与Lua神秘面纱 redis数据库 Redis企业集群高级应用精品教程[图灵学院] Redis权威指南 利用redis + lua解决抢红包高并 ...

  3. angular 表单元素的验证清除问题

    项目中利用了前些时候写的弹出dialog的方式,验证方式用了控件angular-validation(http://www.cnblogs.com/FineDay/p/7255689.html) 验证 ...

  4. C语言中点操作符(.)和箭头操作符(->)

    C语言中点操作符(.)和箭头操作符(->) 点说语法不太准确,许多都称该之为点运算符/操作符,箭头运算符/操作符.但是OC中叫点语法,感觉理解起来还蛮舒服.毕竟基础的C操作符中是 相同点 两个都 ...

  5. 从urllib2的内存泄露看python的GC python引用计数 对象的引用数 循环引用

    这里会发现上述代码是存在内存泄露,造成的原因就是lz与ow这两个变量存在循环引用,Python 不知道按照什么样的安全次序来调用对象的 __del__() 函数,导致对象始终存活在 gc.garbag ...

  6. Java 输入/输出——处理流(BufferedStream、PrintStream、转换流、推回输入流)

    关于使用处理流的优势,归纳起来就是两点:(1)对于开发人员来说,使用处理流进行输入/输出操作更简单:(2)使用处理流执行效率更高. 1.BufferedInputStream/BufferedOutp ...

  7. oracle 11g/12c 密码复杂度验证设置

    ############################################################################### ###### 11g ###### ## ...

  8. 牛客网Wannafly挑战赛25A 因子 数论

    正解:小学数学数论 解题报告: 传送门 大概会连着写几道相对而言比较简单的数学题,,,之后就会比较难了QAQ 所以这题相对而言还是比较水的,,, 首先这种题目不难想到分解质因数趴,, 于是就先对p和n ...

  9. vim 私人快捷键备忘录

    i 上 k 下 j 左 l 右 ( 上移一段 ) 下移一段 * 搜索关键字 d 删除 y 复制 p 粘贴 h 插入 H 头插 o 下开一行 O 上开一行 f 后跳指定关键字 F 前跳指定关键字 e 字 ...

  10. ES6的十大新特性(转)

    add by zhj: 该文章是由国外一哥们写的,由腾讯前端团队翻译,图片中的妹子长得挺好看的,很养眼,嘿嘿.我目前在学习ES6,这篇文章把ES6的 几个主要新特性进行了归纳总结,犹如脑图一般,让人看 ...