ThreadLocal,Thread和ThreadLocalMap分享

简介

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                     return15                 }
16 
17                 if (k == ) {
18                     replaceStaleEntry(key,value,i);
19                     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连不迷路~~

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


问题背景 最近小伙伴提了一个希望提高后台下拉列表可操作性的需求,原因是下拉列表选项过多,每次下拉选择比较费时费力且容易出错,硬着头皮啃了啃前端知识,网上搜寻了一些下拉列表实现的资料,这里总结一下。 P
// n位随机数生成 function randomNum(n) { let sString = &quot;&quot;; let strings = &quot;abcdefghijklmnopq
HTML是HyperText Markup Language的简称,中文名称:超文本标记语言,它是一种用于创建网页的 标准标记语言
层叠样式表(英文全称:Cascading Style Sheets)是一种用来表现HTML或XML(标准通用标记语言的一个子集)等文件样式的计算机语言。
JavaScript 是脚本语言,是一种解释性脚本语言(代码不进行预编译)
本文由葡萄城技术团队原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 一名开发者或设计师通常可以在一秒内指出优秀的设计,但对于糟糕的设计只需最多半
本文由葡萄城技术团队原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 Excel是我们办公中常用的工具 ,它几乎能为我们处理大部分数据,友好的交互
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 原文出处:https://blog.bitsrc.io/8-tips-for-an-awesome-sign
本文由葡萄城技术团队于博客园翻译并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 如果我们自己编写从URL中分析和提取元素的代码,那么有可能会比较痛苦
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 原文出处:https://blog.bitsrc.io/what-is-deno-and-will-it-r
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 原文出处:https://blog.bitsrc.io/4-reasons-to-use-sass-in-y
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 原文出处:https://blog.bitsrc.io/vuejs-3-0-0-beta-features-
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 原文出处:https://blog.bitsrc.io/sessionstorage-and-localst
一直以来,JavaScript使用数组和对象来定义和存放结构化数据, 在这篇文章中,我们将一起深挖另一种对象Map的一切,我们将会去了解它是什么、如何遍历、都包括什么属性和方法以及优缺点是什么。
由于CSS的出现,现在的网站风格已经与它们很早之前的样子有了很大的不同。CSS的出现为原本平平无奇的网页注入了活力。这也是网站的用户体验得到进一步进化的原因。这可能就是当今几乎所有的网站或多或少都在使
自苹果推出了iPhone应用商店以来,App成为了我们生活中不可或缺的一部分,而对于实体业务也是如此,现在各行业都在推出自己的App,但有没有人想过这样一种场景,如果自己的潜在客户还没有安装你的App
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 原文出处:https://blog.bitsrc.io/10-top-chrome-extensions-f
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 原文出处:https://blog.bitsrc.io/do-your-buttons-lead-or-mi
本文由葡萄城技术团队原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 前段时间在开发【葡萄城社区】公众号时有一个功能是需要用网页授权认证地址生成二
本文由葡萄城技术团队原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 HTML5的发展改变了互联网技术趋势,前端热度依旧不减,所以对于应用开发人员