ThreadLocal的简单理解
一、背景
最近有人问我ThreadLocal
是如何做到在每个线程中的值都是隔离的,此处写篇文章来简单记录下。
二、ThreadLocal解决的问题
- 该数据属于该线程
Thread
自身,别的线程无法对其影响。(需要注意:需要调用ThreadLocal的remove方法) - 不存在线程安全问题。(因为
ThreadLocal
类型的变量只有自身的线程可以访问,所以这点是成立的。)
比如:
用户登录成功后,需要将登录用户信息
保存起来,以方便在系统中的任何地方都可以使用到,那么此时就可以使用ThreadLocal
来实现。例如:Spring Security
中的ThreadLocalSecurityContextHolderStrategy
类。
三、如何创建一个ThreadLocal实例
private static final ThreadLocal<String> USER_NAME = new ThreadLocal<>();
ThreadLocal的实例推荐使用private static final
来修饰。
四、ThreadLocal如何做到线程变量隔离
1、理解3个类
ThreadLocal
: 此类提供了一个简单的set
,get
,remove
方法,用于设置,获取或移除 绑定到线程本地变量中的值。ThreadLocalMap
: 这是在ThreadLocal中定义的一个类,可以简单的将它理解成一个Map,不过它的key是WeakReference弱引用类型,这样当这个值没有在别的地方引用时,在发生垃圾回收时,这个map的key
会被自动回收,不过它的值不会被自动回收。static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
// key 弱引用
super(k);
// 值强引用
value = v;
}
}
Thread
:这个是线程类,在这个类中存在一个threadLocals
变量,具体的类型是ThreadLocal.ThreadLocalMap
。
2、看下set方法是如何实现的
public void set(T value) {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取绑定到这个线程自身的 ThreadLocalMap,这个ThreadLocalMap是从Thread类的`threadLocals`变量中获取的
ThreadLocalMap map = getMap(t);
if (map != null) {
// 向map中设置值,key为 ThreadLocal 对象的实例。
map.set(this, value);
} else {
// 如果map不存在,则创建出来。
createMap(t, value);
}
}
通过上方的代码,我们可知: 当我们向ThreadLocal中设置一个值,会经过如下几个步骤:
- 获取当前线程
Thread
- 获取当前线程的
ThreadLocalMap
对象。 - 向
ThreadLocalMap
中设置值,key为ThreadLocal
对象,值为具体的值。
3、看看 get 方法如何实现
public T get() {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取这个线程自身绑定的 ThreadLocalMap 对象
ThreadLocalMap map = getMap(t);
if (map != null) {
// this是ThreadLocal对象,获取Map中的Entry对象
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
// 获取具体的值
T result = (T)e.value;
return result;
}
}
// 设置初始值
return setInitialValue();
}
从上方的get 和 set 方法可以得知,通过往ThreadLocal对象中设置值或获取值,其实是最终操作到Thread对象中的threadLocals字段中,而这个字段是Thread自身的,因此做到了隔离。
五、ThreadLocalMap中的hash冲突是如何处理的
1、ThreadLocal对象的hash值是怎样的
private final int threadLocalHashCode = nextHashCode();
// 该 ThreadLocal 对象自身的hash code值
private final int threadLocalHashCode = nextHashCode();
// 从0开始
private static AtomicInteger nextHashCode = new AtomicInteger();
// 每次递增固定的值
private static final int HASH_INCREMENT = 0x61c88647;
// hash code 值计算
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
从上方的代码中可以,ThreadLocal
类在实例化出来之后,它的hash code值(threadLocalHashCode)
就是固定的,即使ThreadLocal
调用了set
方法,设置了别的值,它的hash code值
也不会发生变化。
此字段threadLocalHashCode
为ThreadLocal
对象的hash值,在ThreadLocalMap
中需要用到这个hash值。
2、解决hash冲突
ThreadLocalMap
解决hash冲突的办法很简单。就是通过线性探测法。如果发生了冲突,就去找数组后面的可用位置。具体看上图。演示的是A和B 2个ThreadLocal对象,然后发生了冲突,A和B存在的位置在那个地方。
六、ThreadLocal内存泄漏
ThreadLocal为什么会存在内存泄漏呢?
这是因为ThreadLocalMap
中的key
是WeakReference
类型,也就是弱引用类型,而弱引用类型的数据在没有外部强引用类型的话,在发生gc
的时候,会自动被回收掉。注意:
此时是key
被回收了,但是value
是没有回收的。因此在ThreadLocalMap
中的Entry[]
中可能存在key
是null
,但是value
是具体的值的对象,因此就发生了内存泄漏。
解决内存泄漏:
当我们使用完ThreadLocal
对象后,需要在适当的时机调用ThreadLocal#remove()
方法。 否则就只有等Thread
自动退出才能清除,如果是使用了线程池,Thread
会重用,清除的机会就更难。
ThreadLocal的简单理解的更多相关文章
- 对ThreadLocal的一些理解
ThreadLocal也是在面试过程中经常被问到的,本文主要从以下三个方面来谈对ThreadLocal的一些理解: ThreadLocal用在什么地方 ThreadLocal一些细节 ThreadLo ...
- ThreadLocal原理简单刨析
ThreadLocal原理简单刨析 ThreadLocal实现了各个线程的数据隔离,要知道数据是如何隔离的,就要从源代码分析. ThreadLocal原理 需要提前说明的是:ThreadLocal只是 ...
- git的简单理解及基础操作命令
前端小白一枚,最近开始使用git,于是花了2天看了廖雪峰的git教程(偏实践,对于学习git的基础操作很有帮助哦),也在看<git版本控制管理>这本书(偏理论,内容完善,很不错),针对所学 ...
- 简单理解Struts2中拦截器与过滤器的区别及执行顺序
简单理解Struts2中拦截器与过滤器的区别及执行顺序 当接收到一个httprequest , a) 当外部的httpservletrequest到来时 b) 初始到了servlet容器 传递给一个标 ...
- [转]简单理解Socket
简单理解Socket 转自 http://www.cnblogs.com/dolphinX/p/3460545.html 题外话 前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公 ...
- Js 职责链模式 简单理解
js 职责链模式 的简单理解.大叔的代码太高深了,不好理解. function Handler(s) { this.successor = s || null; this.handle = funct ...
- Deep learning:四十六(DropConnect简单理解)
和maxout(maxout简单理解)一样,DropConnect也是在ICML2013上发表的,同样也是为了提高Deep Network的泛化能力的,两者都号称是对Dropout(Dropout简单 ...
- Deep learning:四十二(Denoise Autoencoder简单理解)
前言: 当采用无监督的方法分层预训练深度网络的权值时,为了学习到较鲁棒的特征,可以在网络的可视层(即数据的输入层)引入随机噪声,这种方法称为Denoise Autoencoder(简称dAE),由Be ...
- 简单理解dropout
dropout是CNN(卷积神经网络)中的一个trick,能防止过拟合. 关于dropout的详细内容,还是看论文原文好了: Hinton, G. E., et al. (2012). "I ...
随机推荐
- 『忘了再学』Shell基础 — 9、Bash中的特殊符号(一)
目录 1.双单引号 2.双引号 3.$符号 4.反引号 5.$()符号 6.#符号 7.\符号 1.双单引号 '':单引号.在单引号中所有的特殊符号,如$和"`"(反引号)都没有特 ...
- 省掉80%配置时间,这款Mock神器免费又好用
前端的痛苦 作为前端,最痛苦的是什么时候? 每个迭代,需求文档跟设计稿都出来了,静态页面唰唰两天就做完了.可是做前端又不是简单地把后端吐出来的数据放到页面上就完了,还有各种前端处理逻辑啊. 后端接口还 ...
- 基于.NetCore开发博客项目 StarBlog - (3) 模型设计
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...
- 条件表达式和switch语句
一,条件表达式相当于(相当于if-else) 1.条件表达式 ? : 由条件运算符组成条件表达式的一般情况为: 表达式1?表达式2:表达式3: 求值规则:如果表达式的值为真,则以表达式2的值作为条件的 ...
- XCTF练习题---MISC---stegano
XCTF练习题---MISC---stegano flag:flag{1nv151bl3m3554g3} 解题步骤: 1.观察题目,下载附件 2.打开发现是一张PDF图片,尝试转换word无果后,想到 ...
- [洛谷] P2010 [NOIP2016 普及组] 回文日期
点击查看代码 #include<bits/stdc++.h> using namespace std; int data1, data2, ans = 0, sum; int d[13] ...
- [笔记] $f(i)$ 为 $k$ 次多项式,$\sum_{i=0}^nf(i)\cdot q^i$ 的 $O(k\log k)$ 求法
\(f(i)\) 为 \(k\) 次多项式,\(\sum_{i=0}^nf(i)\cdot q^i\) 的 \(O(k\log k)\) 求法 令 \(S(n)=\sum_{i=0}^{n-1}f(i ...
- ONNXRuntime学习笔记(一)
一. DL模型落地步骤 一般情况下,一个DL任务落地的流程一般包含训练和部署两大部分,具体细分我认为可以分为以下几个步骤: 1. 明确任务目标:首先要明确我们最终要达到一个什么样的效果,假设我们的DL ...
- BottomNavigationView点击刷新问题
BottomNavigationView点击刷新问题 问题: 项目中使用了BottomNavigationView作为底部导航栏. 点击item切换的时候发现会有闪烁现象出现. 原因: BottomN ...
- JDK自带线程池学习
JDK自带线程池 线程池的状态 线程有如下状态 RUNNING状态:Accept new tasks and process queued tasks SHUTDOWN状态:Don't accept ...