简介

ThreadLocal的用处

ThreadLocal是为了将数据记录一份到某个线程里,确保该数据线程安全

例如数据库的Connection放入ThreadLocal,一个事务会用到很多DAO,但只能用共同的Connection,这样才能保证事务完整性

所以当某个类的其中一个变量,会被同一个线程多次使用,并且还严格的规定每次都得是这个变量操作

那么就能把这个变量放入ThreadLocal

Spring也是将各种Bean放入ThreadLocal中来确保Bean的“无状态”化

吐槽开始!!

今天翻书看了关于ThreadLocal的介绍,和网上一些关于ThreadLocal的博客,这个原理介绍真的是坑,大错特错

大家可能都看到过下面这种所谓的ThreadLocal简单的实现思路介绍:

完了后还加上一句:

虽然上面的代码清单中的这个ThreadLocal实现版本显得比较简单粗爆,但其目的主要在与呈现JDK中所提供的ThreadLocal类在实现上的思路

上面这样简化ThreadLocal实现根本错的离谱

不仅是有的博客这样,包括书本也是这样介绍的,传播知识给他人,的确可以简化代码实现,但不等于更改了正确的实现思路!

这样会误导他人对ThreadLocal的进一步学习

ThreadLocal真正的实现方式

先说总结,跟上面错误做对比

1.ThreadLocalMap 别看有个Map结尾,其实压根就是重新实现的类

跟Map没半毛钱关系,没实现Map接口的,没用HashMap,别觉得根据key找value就只能使用map了

2.线程根据key找对应的value,这个key并不是线程id,而是ThreadLocal类

为什么,因为ThreadLocalMap是存放在线程里的,每个线程都只有一个只属于自己的ThreadLocalMap

这样的话存个毛的线程id,有什么意义?

揭开Thread,ThreadLocal,ThreadLocalMap真正的关系

首先进入ThreadLocal,发现如下

ThreadLocalMap实在ThreadLocal里实现的

直接找到ThreadLocal的set()方法

    public void set(T value) {
Thread t = Thread.currentThread(); //获得当前线程
ThreadLocalMap map = getMap(t); //将当前线程作为参数传入,来获取ThreadLocalMap
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

然后进入getMap()方法

    ThreadLocalMap getMap(Thread t) {
return t.threadLocals; //追踪后发现t.threadLocals就是 ThreadLocal.ThreadLocalMap threadLocals;
}

如果得到的map为null,那么说明是第一次,走createMap方法创建

 void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue); //看到了吧,ThreadLocalMap直接是给Thread保存的
}

进入new ThreadLocalMap方法,这里注意,传入的this就是指ThreadLocal

 ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {   //看到参数名字没,firstKey,ThreadLocal传进来是当作key值的!
table = new Entry[INITIAL_CAPACITY];                 //table其实是private Entry[] table; 一个数组而已
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}

ThreadLocalMap没有实现Map接口,跟Map没半毛钱关系,至于Entry是什么,我们看看

static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value; Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}

首先WeakReference是指弱引用的意思,继承了这玩意有以下效果:

当一个对象仅仅被weak reference(弱引用)指向, 而没有任何其他strong reference(强引用)指向的时候, 如果这时GC运行, 那么这个对象就会被回收,不论当前的内存空间是否足够,这个对象都会被回收。

好处在于,如果某个ThreadLocal被回收了,那么ThreadLocalMap的这个Key在下次GC()的时候也会被回收(不然就造成内存泄露了,这个key永远不会被调用)

Entry是一个ThreadLocalMap的内部类,跟Map的Entry实现有点像,有key和value的定义(俗称 桶)

ok,我们发现,ThreadLocalMap创建后,就是初始化一个Entry的数组,生成一个Entry将ThreadLocal作为key值,将要存的值作为value放入其中存好

那么假设现在ThreadLocalMap已经存在,走的是第一个分支,直接set,我们看看他怎么实现的

 1    private void set(ThreadLocal<?> key, Object value) {
2
3 Entry[] tab = table;
4 int len = tab.length;
5 int i = key.threadLocalHashCode & (len-1);
6
7 for (Entry e = tab[i];
8 e != null;
9 e = tab[i = nextIndex(i, len)]) {
10 ThreadLocal<?> k = e.get();
11
12 if (k == key) {
13 e.value = value;
14 return;
15 }
16
17 if (k == null) {
18 replaceStaleEntry(key, value, i);
19 return;
20 }
21 }
22
23 tab[i] = new Entry(key, value);
24 int sz = ++size;
25 if (!cleanSomeSlots(i, sz) && sz >= threshold)
26 rehash();
27 }

看到第7行的for没有,直接遍历方才说的Entry数组,将ThreadLocal取出来比较(相当于key比较),匹配就设置value,没这个key就存起来

ThreadLocalMap为啥不用HashMap而是自己数组实现

有key和value这个概念出现,也不是一定要用HashMap这些的,为什么用数组

个人觉得是省开销,创建Map对象的开销和使用Map的开销,毕竟ThreadLocalMap初始默认长度为16,而真实情况中一个线程不会有这么本地变量要保存

所以,当使用ThreadLocal来存的时候,ThreadLocal会创建一个ThreadLocalMap给调用它的线程,自己作为key值去保存

取值的时候,ThreadLocal拿Thread里保存的ThreadLocal,然后将自身作为key值去取值

为什么ThreadLocalMap的代码不放在Thread中

网上有个答案我觉得很合理:

将ThreadLocalMap定义在Thread类内部看起来更符合逻辑
但是ThreadLocalMap并不需要Thread对象来操作,所以定义在Thread类内只会增加一些不必要的开销。
定义在ThreadLocal类中的原因是ThreadLocal类负责ThreadLocalMap的创建和使用
总的来说就是,ThreadLocalMap不是必需品,定义在Thread中增加了成本,定义在ThreadLocal中按需创建。

线程不一定都用到ThreadLocal的哦,如果不使用,就不会被赋值一个ThreadLocalMap

换个思维,这其实也是种不错的设计模式:

一个工具类把自身当作唯一标识,去操作工具类本身的方法,只需要将数据记录给调用它的类就好

ThreadLocal的内存泄露问题

正常来说,我们创建一个线程,跑完后会销毁,自动调用ThreadLocal的remove()方法,清除ThreadLocalMap的内容

但是,实际中我们是使用线程池的,而线程跑完后会返回线程池中,并不会销毁

这时候的ThreadLocalMap的内容就还在的(内存就是这里泄露啦)

所以,在线程池中用ThreadLocal,记得run()要跑完时用下remove()方法清除ThreadLocalMap中的key

至此分享完毕啦,希望大家点点赞,或者一键3连不迷路~~

ThreadLocal原理记录,别被坑了!!的更多相关文章

  1. ThreadLocal原理及其实际应用

    前言 java猿在面试中,经常会被问到1个问题: java实现同步有哪几种方式? 大家一般都会回答使用synchronized, 那么还有其他方式吗? 答案是肯定的, 另外一种方式也就是本文要说的Th ...

  2. ThreadLocal学习记录

    ThreadLocal简介 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的 ...

  3. java基础解析系列(七)---ThreadLocal原理分析

    java基础解析系列(七)---ThreadLocal原理分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)-- ...

  4. 简析ThreadLocal原理及应用

    简析ThreadLocal原理及应用 原创: 东晨雨 JAVA万维猿圈 4月17日 ThreadLocal的源码加上注释不超过八百行,源码结构清晰,代码也比较简洁.ThreadLocal可以说是Jav ...

  5. ThreadLocal原理简单刨析

    ThreadLocal原理简单刨析 ThreadLocal实现了各个线程的数据隔离,要知道数据是如何隔离的,就要从源代码分析. ThreadLocal原理 需要提前说明的是:ThreadLocal只是 ...

  6. ThreadLocal 原理和使用场景分析

    ThreadLocal 不知道大家有没有用过,但至少听说过,今天主要记录一下 ThreadLocal 的原理和使用场景. 使用场景 直接定位到 ThreadLocal 的源码,可以看到源码注释中有很清 ...

  7. ThreadLocal原理分析与使用场景

    什么是ThreadLocal变量 ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本.这里有几点需要注意: 因为每个 Thr ...

  8. 03、Swagger2和Springmvc整合详细记录(爬坑记录)

    时间 内容 备注 2018年6月18日 基本使用 spirngmvc整合swagger2 开始之前这个系列博文基本是,在项目的使用中一些模块的内容记录,但是后期逐渐优化,不单单是整合内容. swagg ...

  9. ThreadLocal原理分析

    本文结构 ThreadLocal简介 (简要说明ThreadLocal的作用) ThreadLocal实现原理(说明ThreadLocal的常用方法和原理) ThreadLocalMap的实现 (说明 ...

随机推荐

  1. 315. Count of Smaller Numbers After Self(二分或者算法导论中的归并求逆序数对)

    You are given an integer array nums and you have to return a new counts array. The counts array has ...

  2. C#高级编程之泛型二(泛型类型约束、类型、反射与泛型)

    泛型类型约束 简言之:对泛型类型进行约束,细化,限定. MSDN的定义:泛型定义中的 where 子句指定对用作泛型类型.方法.委托或本地函数中类型参数的参数类型的约束,意思就是可以有泛型类.泛型方法 ...

  3. 【鸿蒙开发板试用报告】用OLED板实现FlappyBird小游戏(上)

    总是做各种Demo,是时候做个什么小应用来练练手了.踌躇了很久,果然还是搞个小游戏才有意思.想到几年前风靡全球的FlappyBird,一个屏幕一个按钮就足够了,正好适合.OLED屏幕.按键的驱动已经有 ...

  4. FTP漏洞利用复现

    目录 FTP弱口令漏洞 FTP后门漏洞利用 FTP弱口令漏洞 漏洞描述 FTP弱口令或匿名登录漏洞,一般指使用FTP的用户启用了匿名登录功能,或系统口令的长度太短.复杂度不够.仅包含数字.或仅包含字母 ...

  5. vue项目中h5移动端中通过flex布局实现首尾固定,中间滚动(借鉴)

    html中 <div class="flexLayoutr"> <div class="div_head"></div> & ...

  6. java开发三年,Java中接口的使用你得知道,不然你凭什么涨薪

    接口概述: 接口是Java语言中的一种引用类型,是方法的"集合",所以接口的内部主要就是定义方法,包含常量,抽象方法(JDK 7及以前),额外增加默认方法和静态方法(JDK 8), ...

  7. 新鲜出炉!2020年最新java面试题大全,面试突击必备!

    前言 发现网上很多Java面试题都没有答案,所以花了很长时间搜集整理出来了一套Java面试题,希望对大家有帮助哈~ 打算这几天每天更新15~20题.(这样有助于你们阅读和理解!)我们先从简单的开始 1 ...

  8. 今天谁也别想阻止我好好学习!「CDR 6·18特惠倒计时2天!」

    前几天小编刷抖音,一个以农夫山泉为创作背景的服装原创视频 让我为之一震 这个自称以捡瓶子为生的服装设计师 让我产生了极为浓烈的兴趣 细扒这位小姐姐的视频 她用身边的常见物品 脑洞大开的画出了一系列插画 ...

  9. 左右声道音频怎么制作,用Vegas就对啦

    一款优秀的视频剪辑软件,不仅有高水平的视频制作功能,它的音频编辑功能也是必不可少的.Vegas就是这么一款软件,同时具备视频制作特效制作的同时,还能帮助制作轨道音频效果. 下面,就让小编带大家去学习, ...

  10. 从这三方面优化你的电脑,保持Mac运行流畅

    使用着Mac系统的用户都知道,Mac OS的各方面性能都很好,特别是流畅性,有人说不用清理垃圾也能流畅地使用Mac,但这的确是夸张了.电脑使用的时间长了,它的性能总会越来越退步,这其中有着系统垃圾拖累 ...