今天在看Spring 3.x企业应用开发实战,第九章 Spring的事务管理,9.2.2节ThreadLocal的接口方法时,书上有提到Threadlocal的简单实现,我就去看了下JDK1.8的Threadlocal的源码。发现实现方式与书中讲的并不相同,同时在网上搜索了一下,发现有比较多的人理解错了。

先看一下容易误导的解释:在ThreadLocal类中有一个Map对象,这个Map以每个Thread对象为键,保存了这个线程对应局部变量值,对应的实现方式如下:

  1. public class SimpleThreadLocal {
  2. private Map valueMap = Collections.synchronizedMap(new HashMap());
  3. public void set(Object newValue) {
  4. valueMap.put(Thread.currentThread(), newValue);//①键为线程对象,值为本线程的变量副本
  5. }
  6. public Object get() {
  7. Thread currentThread = Thread.currentThread();
  8. Object o = valueMap.get(currentThread);//②返回本线程对应的变量
  9. if (o == null && !valueMap.containsKey(currentThread)) {//③如果在Map中不存在,放到Map 中保存起来。
  10. o = initialValue();
  11. valueMap.put(currentThread, o);
  12. }
  13. return o;
  14. }
  15. public void remove() {
  16. valueMap.remove(Thread.currentThread());
  17. }
  18. public Object initialValue() {
  19. return null;
  20. }
  21. }

为什么不按照那种有误的方法实现呢?

看起来似乎是不同线程获取了各自的值,但是这些值并没有线程独立。线程A可以操作线程B对应的值。如果某个线程将保存这些值的Map置为null了,那么其他线程也无法访问了。

实际上是怎样的呢

我们看ThreadLocal的get()方法源码。可以看到获取Threadlocal中的值,是先通过当前线程的线程对象t,获取t的ThreadlocalMap属性对象,然后再以Threadlocal对象为键,去获取ThreadlocalMap中的值。

  1. public T get() {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);//获取线程对象的属性
  4. if (map != null) {
  5. ThreadLocalMap.Entry e = map.getEntry(this);//这里的this是指Threadlocal对象,Threadlocal在类中通常以静态属性出现,所以多个线程的Threadlocal指向同一个对象。
  6. if (e != null) {
  7. @SuppressWarnings("unchecked")
  8. T result = (T)e.value;
  9. return result;
  10. }
  11. }
  12. return setInitialValue();
  13. }
  14. ThreadLocalMap getMap(Thread t) {
  15. return t.threadLocals;
  16. }

同时我们查看ThreadLocal源码中定义的静态类ThreadLocalMap,其实底层封装的是一个Entry数组,获取方式和普通的HashMap不太一样,如果没有命中,就直接通过线性搜索,因为ThreadLocalMap需要保存的Entry并不会太多。

  1. private Entry getEntry(ThreadLocal<?> key) {
  2. int i = key.threadLocalHashCode & (table.length - 1);
  3. Entry e = table[i];
  4. if (e != null && e.get() == key)
  5. return e;
  6. else
  7. return getEntryAfterMiss(key, i, e);
  8. }
  9. private void set(ThreadLocal<?> key, Object value) {
  10. // We don't use a fast path as with get() because it is at
  11. // least as common to use set() to create new entries as
  12. // it is to replace existing ones, in which case, a fast
  13. // path would fail more often than not.
  14. Entry[] tab = table;
  15. int len = tab.length;
  16. int i = key.threadLocalHashCode & (len-1);
  17. for (Entry e = tab[i];
  18. e != null;
  19. e = tab[i = nextIndex(i, len)]) {
  20. ThreadLocal<?> k = e.get();
  21. if (k == key) {
  22. e.value = value;
  23. return;
  24. }
  25. if (k == null) {
  26. replaceStaleEntry(key, value, i);
  27. return;
  28. }
  29. }

通过ThreadLocal,每个线程保存自身的数据,不能访问到其他线程的数据。

ThreadLocalMap的Entry使用ThreadLocal的WeakReference引用作为Key值,当所有线程运行出ThreadLocal的作用域时,即没有强引用ThreadLocal时,ThreadLocal就会被回收。

  1. static class Entry extends WeakReference<ThreadLocal<?>> {
  2. /** The value associated with this ThreadLocal. */
  3. Object value;
  4. Entry(ThreadLocal<?> k, Object v) {
  5. super(k);
  6. value = v;
  7. }
  8. }

不是Threadlocal为每个线程提供了独立的变量,而是每个线程自己带了自己独立的变量。

关于内存泄漏

关于ThreadLocalMap的内存泄漏:如果一个ThreadLocal的生命周期结束,即在ThreadLocal所处的类中没有了强引用,而Thread没有结束,在Thread的threadLocals成员变量中,会有一个Entry使用弱引用引用了ThreadLocal作为key,因为是弱引用,这个key将被回收。而value是强引用,看起来是会造成泄漏,但是在ThreadLocalMap的set和get方法中,有一些释放的方法。具体的我也不太懂。

还是老老实实使用ThreadLocal的remove方法比较好。

  1. public void remove() {
  2. ThreadLocalMap m = getMap(Thread.currentThread());
  3. if (m != null)
  4. m.remove(this);
  5. }

Threadlocal源码分析以及其中WeakReference作用分析的更多相关文章

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

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

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

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

  3. ThreadLocal源码及相关问题分析

    前言 在高并发的环境下,当我们使用一个公共的变量时如果不加锁会出现并发问题,例如SimpleDateFormat,但是加锁的话会影响性能,对于这种情况我们可以使用ThreadLocal.ThreadL ...

  4. ThreadLocal源码分析-黄金分割数的使用

    前提 最近接触到的一个项目要兼容新老系统,最终采用了ThreadLocal(实际上用的是InheritableThreadLocal)用于在子线程获取父线程中共享的变量.问题是解决了,但是后来发现对T ...

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

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

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

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

  7. 【JAVA】ThreadLocal源码分析

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

  8. 并发-ThreadLocal源码分析

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

  9. Java -- 基于JDK1.8的ThreadLocal源码分析

    1,最近在做一个需求的时候需要对外部暴露一个值得应用  ,一般来说直接写个单例,将这个成员变量的值暴露出去就ok了,但是当时突然灵机一动(现在回想是个多余的想法),想到handle源码里面有使用过Th ...

随机推荐

  1. 工作中常用的Git操作

    粘贴自:微信公众号:程序员共成长 分支操作: git branch 创建分支 git branch -b 创建并切换到新建的分支上 git checkout 切换分支 git branch 查看分支列 ...

  2. 使用Tarjan进行缩点无向图

    int From[maxn],Laxt[maxn],To[maxn<<2],Next[maxn<<2],cnt; int low[maxn],dfn[maxn],times,q ...

  3. java线程基础巩固---线程生命周期以及start方法源码剖析

    上篇中介绍了如何启动一个线程,通过调用start()方法才能创建并使用新线程,并且这个start()是非阻塞的,调用之后立马就返回的,实际上它是线程生命周期环节中的一种,所以这里阐述一下线程的一个完整 ...

  4. Vue项目中的文件导出

    项目中涉及到文件导出,分xml和excel导出.不同的文件导出格式不同,需要根据文件类型判断导出格式. exportAllData(val){ //全部导出 if(!val){ this.export ...

  5. 【经典dp 技巧】8.13序列

    经典的拆绝对值 题目大意 给定$n$个具有顺序的序列,允许对每个序列循环移动.记第$i$个序列尾元素为$x$,$i+1$个序列首元素为$y$,定义其连接收益为$|x-y|*i$,求$n$个序列连接最大 ...

  6. Mysql判断是否某个字符串在某字符串字段的4种方法

    方法一:like SELECT * FROM 表名 WHERE 字段名 like "%字符%"; 方法二:find_in_set() 利用mysql 字符串函数 find_in_s ...

  7. spark为什么比hadoop的mr要快?

    1.前言 Spark是基于内存的计算,而Hadoop是基于磁盘的计算:Spark是一种内存计算技术. 但是事实上,不光Spark是内存计算,Hadoop其实也是内存计算. Spark和Hadoop的根 ...

  8. HDU - 5528Count a * b 数学公式推导

    HDU - 5528Count a * b 照着这个写的,忘了哪个大佬博客里的了, 但很多初学者,像我一样可能不懂其中一些符号的意义,还有为什么可以这样推导,所以我自己又推导了一下,并用自己的观点解释 ...

  9. spring boot+idea实现程序热部署

    pring为开发者提供了一个名为spring-boot-devtools的模块来使Spring Boot应用支持热部署,提高开发者的开发效率,无需手动重启Spring Boot应用. devtools ...

  10. element-ui练习使用总结

    <el-row> <el-col class="borderRed" :span="24"> <div class="g ...