ThreadLocal原理详解——终于弄明白了ThreadLocal
目录
概述
在java学习生涯中可能很多人都会听到ThreadLocal变量,从字面上理解ThreadLocal就是“线程局部变量”的意思。简单的说就是,一个ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的(每个线程都只能看到自己线程的值)。可能一开始把这句话放出来很难理解,那我们就继续往后面看吧。
API介绍
再学习一个类之前我们需要了解一个类的API,这也是我们学习类的入口。而ThreadLocal类的API相当简单。

在这里面比较重要的就是,get、set、remove了,这三个方法是对这个变量进行操作的关键。set用于赋值操作,get用于获取变量中的值,remove就是删除当前这个变量的值。需要注意的是initialValue方法会在第一次调用时被触发,用于初始化当前变量值,例如在下列代码中我们需要创建一个ThreadLocal<Connection>,用于创建一个与线程绑定的Connection对象:
ThreadLocal<Connection> connection = new ThreadLocal<Connection>(){
public Connection initialValue(){
return DriverManager.getConnection(...);
}
};
为什么我们将ThreadLocal说成变量,我们姑且可以这么理解,每个ThreadLocal实例中都可以保存一个值(基本数据类型值或者引用类型的引用值),而内部保存的值是可以修改的,而这样的特性与变量的特性及其相似,变量不就是用来保存一个值的吗?
也就是说每一个ThreadLocal实例就类似于一个变量名,不同的ThreadLocal实例就是不同的变量名,它们内部会存有一个值(暂时这么理解)在后面的描述中所说的“ThreadLocal变量或者是线程变量”代表的就是ThreadLocal类的实例。
这里还需要介绍一下initialValue方法,我么都知道在Java中成员变量都会有默认值,而ThreadLocal做变量也会有默认值,那我们可以通过重写initialValue方法指定ThreadLocal变量的初始值。默认情况下initialValue返回的是null。
ThreadLocal的理解
说完了ThreadLocal类的API了,那我们就来动手实践一下了,来理解前面没有理解的那句话:一个ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的(每个线程都只能看到自己线程的值)
public class ThreadLocalTest {
private static ThreadLocal<Integer> num = new ThreadLocal<Integer>() {
// 重写这个方法,可以修改“线程变量”的初始值,默认是null
@Override
protected Integer initialValue() {
return 0;
}
};
public static void main(String[] args) {
// 创建一号线程
new Thread(new Runnable() {
@Override
public void run() {
// 在一号线程中将ThreadLocal变量设置为1
num.set(1);
System.out.println("一号线程中ThreadLocal变量中保存的值为:" + num.get());
}
}).start();
// 创建二号线程
new Thread(new Runnable() {
@Override
public void run() {
num.set(2);
System.out.println("二号线程中ThreadLocal变量中保存的值为:" + num.get());
}
}).start();
//为了让一二号线程执行完毕,让主线程睡500ms
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("主线程中ThreadLocal变量中保存的值:" + num.get());
}
}
稍微解释一下上面的代码:
在类中创建了一个静态的“ThreadLocal变量”,在主线程中创建两个线程,在这两个线程中分别设置ThreadLocal变量为1和2。然后等待一号和二号线程执行完毕后,在主线程中查看ThreadLocal变量的值。
程序结果及分析

程序结果重点看的是主线程输出的是0,如果是一个普通变量,在一号线程和二号线程中将普通变量设置为1和2,那么在一二号线程执行完毕后在打印这个变量,输出的值肯定是1或者2(到底输出哪一个由操作系统的线程调度逻辑有关)。但使用ThreadLocal变量通过两个线程赋值后,在主线程程中输出的却是初始值0。在这也就是为什么“一个ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的”,每个线程都只能看到自己线程的值,这也就是ThreadLocal的核心作用:实现线程范围的局部变量。
ThreadLocal的原理分析
老规矩我们还是将最后结论摆在前面,每个Thread对象都有一个ThreadLocalMap,当创建一个ThreadLocal的时候,就会将该ThreadLocal对象添加到该Map中,其中键就是ThreadLocal,值可以是任意类型。这句话看不懂很正常,等我们一起看完源码以后就明白了。
此时就需要纠正前面提到的错误观点了,前面我们的理解是所有的常量值或者是引用类型的引用都是保存在ThreadLocal实例中的,但实际上不是的,这种说法只是让我们更好的理解ThreadLocal变量这个概念。向ThreadLocal存入一个值,实际上是向当前线程对象中的ThreadLocalMap存入值,ThreadLocalMap我们可以简单的理解成一个Map,而向这个Map存值的key就是ThreadLocal实例本身。

也就是说,想要存入的ThreadLocal中的数据实际上并没有存到ThreadLocal对象中去,而是以这个ThreadLocal实例作为key存到了当前线程中的一个Map中去了,获取ThreadLocal的值时同样也是这个道理。这也就是为什么ThreadLocal可以实现线程之间隔离的原因了。
总结
ThreadLocal的作用:实现线程范围内的局部变量,即ThreadLocal在一个线程中是共享的,在不同线程之间是隔离的。
ThreadLocal的原理:ThreadLocal存入值时使用当前ThreadLocal实例作为key,存入当前线程对象中的Map中去。最开始在看源码之前,我以为是以当前线程对象作为key将对象存入到ThreadLocal中的Map中去....
各位大佬,原创不易呀,路过的点个赞!!!
ThreadLocal原理详解——终于弄明白了ThreadLocal的更多相关文章
- select用法&原理详解(源码剖析)(转)
今天遇到了在select()前后fd_set的变化问题,查了好久终于找到一个有用的帖子了,很赞,很详细!!原文链接如下: select用法&原理详解(源码剖析) 我的问题是: 如下图示:在se ...
- Nginx与PHP-FPM运行原理详解
目录 1. 代理与反向代理 1. 正向代理:访问google.com 2. 反向代理:通过反向代理实现负载均衡 2. 初识Nginx与PHP-FPM 1. Nginx是什么 2. CGI与FastCG ...
- Storm概念、原理详解及其应用(一)BaseStorm
本文借鉴官文,添加了一些解释和看法,其中有些理解,写的比较粗糙,有问题的地方希望大家指出.写这篇文章,是想把一些官文和资料中基础.重点拿出来,能总结出便于大家理解的话语.与大多数“wordcount” ...
- JSPatch实现原理详解<二>
本文转载至 http://blog.cnbang.net/tech/2855/ 距离上次写的<JSPatch实现原理详解>有一个月的时间,在这段时间里 JSPatch 在不断地完善和改进, ...
- 0614MySQL的InnoDB索引原理详解
转自http://www.cnblogs.com/shijingxiang/articles/4743324.html MySQL的InnoDB索引原理详解 http://www.admin10000 ...
- CentOS 6.5 iptables原理详解以及功能说明
CentOS 6.5 iptables原理详解以及功能说明 来源 https://blog.51cto.com/tanxw/1389114 前言 iptables其实就是Linux下的一个开源的信息过 ...
- sso单点登录原理详解
sso单点登录原理详解 01 单系统登录机制 1.http无状态协议 web应用采用browser/server架构,http作为通信协议.http是无状态协议,浏览器的每一次请求,服务 ...
- 通过 JFR 与日志深入探索 JVM - TLAB 原理详解
全系列目录:通过 JFR 与日志深入探索 JVM - 总览篇 什么是 TLAB? TLAB(Thread Local Allocation Buffer)线程本地分配缓存区,这是一个线程专用的内存分配 ...
- I2C 基础原理详解
今天来学习下I2C通信~ I2C(Inter-Intergrated Circuit)指的是 IC(Intergrated Circuit)之间的(Inter) 通信方式.如上图所以有很多的周边设备都 ...
- Zigbee组网原理详解
Zigbee组网原理详解 来源:互联网 作者:佚名2015年08月13日 15:57 [导读] 组建一个完整的zigbee网状网络包括两个步骤:网络初始化.节点加入网络.其中节点加入网络又包括两个 ...
随机推荐
- 千字干货分享:一文教你ABI增强分析,BI的未来就在这里!
自2017年以来,智能概念开始出现,各类商业智能BI应用的使用门槛逐渐降低,商业智能BI制造商主要竞争增强分析的能力.<2020年Gartner分析与BI平台魔法象限报告>指出,2020年 ...
- opencv读取中文路径图片
点击查看代码 img = cv2.imdecode(np.fromfile(filename, dtype=np.uint8), cv2.IMREAD_GRAYSCALE)
- k8s之hostPath存储卷
一.简介 hostPath:用于将目录从工作节点的文件系统挂载到pod中. 数据的生命周期与节点相同.我们知道,虽然hostPath卷实现pod中数据存储到节点的文件系统中,但是pod的调度不是固定的 ...
- 重新点亮linux 命令树————screen 命令和系统日志[二十四]
前言 简单介绍一下screen 正文 因为我们终端关闭后,终端就消失了,故而希望有终端保持. 1.yum install screen 进行安装. 2.使用screen 进行进入 3.然后打开tail ...
- 堡垒机安装pytorch,mmcv,mmclassification,并训练自己的数据集
堡垒机创建conda环境,并激活进入环境 conda create -n mmclassification python=3.7 conda activate mmclassification 堡垒机 ...
- SELECT...FROM 表 a,( SELECT...FROM...WHERE...) tc...的一些注意以及多字段之间的模糊查询
将sql查询结果作为一个表来查询的时候的一些注意事项 因为工作,发现了这种sql的写法,但是有的时候感觉并不是自己想要的结果,自己试着玩了属于是 简单来说,这个查询并不是拼接结果的,而是将结果按照一个 ...
- 国庆集训 Day1 复盘笔记
9.25 \({\color{Green} \mathrm{A\ -\ Powered\ Addition}}\) 只要把序列扫一遍,然后求出目前最大值与当前值的差的最大值 \(x\),再 \(log ...
- 以“升舱”之名,谈谈云原生数据仓库AnalyticDB的核心技术
简介: 企业级云原生数据仓库AnalyticDB提出了升舱计划,旨在承担和帮助金融.运营商.政务等行业构建下一代数据管理和分析系统,以应对不断增长的数据规模,业务数字化转型,和传统数仓替换升级需求.7 ...
- 用python编写向通信产品发送AT指令的程序实例
一.安装pyserial包pip install pyserial 二.实例代码 # -*- coding: utf-8 -*- import time import hashlib from ser ...
- dotnet 6 已知问题 获取 CultureInfo.NumberFormat 可能抛出 IndexOutOfRangeException 异常
本文记录一个 dotnet 6 已知问题,准确来说这是一个在 dotnet 5 引入的问题,到 dotnet 6.0.12 还没修.在获取 CultureInfo.NumberFormat 属性时,在 ...