ThreadLocal 本地线程变量详解
概述
ThreadLocal 意为本地线程变量,即该变量只属于当前线程,对其他线程隔离
我们知道,一个普通变量如果被多线程访问会存在存在线程安全问题,这时我们可以使用 Synchronize 来保证该变量某一时刻只能有一个线程访问,从而解决并发安全问题
但如果这个变量并不需要被共享,那么就可以使用 ThreadLocal 为每个线程提供一个完全独立的变量副本,每个线程只操作自身拥有的副本,彼此互不干扰
简而言之,Synchronized 用于线程间的数据共享,同步机制采用采用时间换空间的方式,而 ThreadLocal 则用于线程间的数据隔离,采用空间换时间的方式
ThreadLocal 使用
public class ThreadLocalTest {
// 有个User对象需要在不同线程之间进行隔离访问,可以定义ThreadLocal如下
static ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
// 设置线程本地变量的内容
public void setUser(User user) {
userThreadLocal.set(user);
}
public void getUser() {
// 获取线程本地变量的内容
User user = userThreadLocal.get();
user.setUsername(user.getUsername + "-" + Thread.currentThread().getName());
System.out.println(user.getUsername());
// 移除线程本地变量
userThreadLocal.remove();
}
}
测试代码如下
public class DemoTest {
public static void main(String[] args) {
ThreadLocalTest threadLocalTest = new ThreadLocalTest();
for(int i = 0; i < 100; i++) {
Thread thread = new Thread(() -> {
User user = new User();
user.setUsername("小明");
threadLocalTest.setUser(user);
threadLocalTest.getUser();
});
thread.setName(String.valueOf(i));
thread.start();
}
}
}
ThreadLocal 原理
首先,Java 中的线程是一个 Thread 类的实例对象,对象可以定义私有的成员变量,这也是 ThreadLocal 能实现线程本地变量的基础
在 Thread 类中定义了一个 Map 类型的成员变量,用来保存该线程的所有本地变量
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap 的 Entry 的定义如下,key 为 ThreadLocal 对象,v 就是我们要在线程之间隔离的对象
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocal::set 方法的源码如下:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
使用 set 方法赋值时,首先会获取当前线程 thread,并获取 thread 线程的 ThreadLocalMap 属性。如果 map 属性不为空,则直接更新 value 值,key 就是自身的 ThreadLocal,如果 map 为空,则实例化 threadLocalMap,并将 value 值初始化
ThreadLocal::get 方法的源码如下:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
使用 get 方法获取值,也是首先获取当前线程,再获取线程 ThreadLocalMap,如果 map 不为空就用自身 ThreadLocal 为 key 从 map 获取对应的 value
ThreadLocal::remove 方法的源码如下:
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null) {
m.remove(this);
}
}
remove 方法也是获取当前线程并获取 ThreadLocalMap,再将自身 ThreadLocal 为 key 对应的 value 移除
综合以上,我们知道 ThreadLocalMap 是线程的一个属性值,用来保存该线程的本地变量。ThreadLocal 能操作当前线程的 ThreadLocalMap,具体做法是以自身为 key 在 map 中存取值。因为每个线程的 ThreadLocalMap 都是独立的,所以每次使用 ThreadLocal 存取值都仅限于当前的线程,不会影响其他线程
ThreadLocal 内存泄露问题
内存泄露问题:指程序中动态分配的堆内存由于某种原因没有被释放或者无法释放,造成系统内存的浪费,导致程序运行速度减慢或者系统奔溃等严重后果。内存泄露堆积将会导致内存溢出
观察使用 ThreadLocal 时的内存布局

当 ThreadLocal Ref 被回收了,由于在 Entry 使用的是强引用,在 Current Thread 还存在的情况下就存在着到达 Entry 的引用链,无法清除掉 ThreadLocal 的内容,同时 Entry 的 value 也同样会被保留,也就是说使用了强引用会出现内存泄露问题
为此,ThreadLocal 在 Entry 使用了弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
// Entry继承WeakReference,将key传入父级构造方法,从而形成弱引用
super(k);
value = v;
}
}
再观察使用软引用后的内存布局

当 ThreadLocal Ref 被回收,由于在 Entry 使用的是弱引用,因此在下次垃圾回收的时候就会将 ThreadLocal 对象清除
但由于 ThreadLocalMap 仍然存在 Current Thread Ref 这个强引用,Entry 中 value 的值仍然无法清除,还是存在内存泄露的问题,虽然当线程生命周期结束,Current Thread Ref 这个强引用也会随之消失,value 会在下一次垃圾回收被清除,但如果使用线程池,那么线程会被重新放回线程池等待复用,那么强引用就会一直存在。因此,我们在每次在使用完之后需要手动的 remove 掉 Entry 对象
ThreadLocal 本地线程变量详解的更多相关文章
- ThreadLocal本地线程变量的理解
一般的Web应用划分为展现层.服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用.在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程. ...
- Java 类 ThreadLocal 本地线程变量
前言:工作中将要使用ThreadLocal,先学习总结一波.有不对的地方欢迎评论指出. 定义 ThreadLocal并不是一个Thread,而是Thread的局部变量.这些变量不同于它们的普通对应物, ...
- Java Concurrency - ThreadLocal, 本地线程变量
共享数据是多线程应用最常见的问题之一,但有时我们需要为每个线程保存一份独立的变量.Java API 提供了 ThreadLocal 来解决这个问题. 一个 ThreadLocal 作用的例子: imp ...
- mysql show variables系统变量详解
mysql系统变量详解 mysqld服务器维护两种变量.全局变量影响服务器的全局操作.会话变量影响具体客户端连接相关操作. 服务器启动时,将所有全局变量初始化为默认值.可以在选项文件或命令行中指定的选 ...
- [Spark内核] 第36课:TaskScheduler内幕天机解密:Spark shell案例运行日志详解、TaskScheduler和SchedulerBackend、FIFO与FAIR、Task运行时本地性算法详解等
本課主題 通过 Spark-shell 窥探程序运行时的状况 TaskScheduler 与 SchedulerBackend 之间的关系 FIFO 与 FAIR 两种调度模式彻底解密 Task 数据 ...
- Java性能分析之线程栈详解与性能分析
Java性能分析之线程栈详解 Java性能分析迈不过去的一个关键点是线程栈,新的性能班级也讲到了JVM这一块,所以本篇文章对线程栈进行基础知识普及以及如何对线程栈进行性能分析. 基本概念 线程堆栈也称 ...
- nginx源码分析线程池详解
nginx源码分析线程池详解 一.前言 nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影响.但是经常会有人问道,n ...
- APP漏洞扫描器之本地拒绝服务检测详解
APP漏洞扫描器之本地拒绝服务检测详解 阿里聚安全的Android应用漏洞扫描器有一个检测项是本地拒绝服务漏洞的检测,采用的是静态分析加动态模糊测试的方法来检测,检测结果准确全面.本文将讲一下应用漏洞 ...
- net core体系-web应用程序-4net core2.0大白话带你入门-5asp.net core环境变量详解
asp.net core环境变量详解 环境变量详解 Windows操作系统的环境变量在哪设置应该都知道了. Linux(centos版本)的环境变量在/etc/profile里面进行设置.用户级的 ...
- Maya 常用环境变量详解
Maya 常用环境变量详解 前言: Maya 的环境变量让用户可以很方便的自定义 Maya 的功能. 在 Maya 的 Help 帮助文档中有专门的一个章节< Environment Varia ...
随机推荐
- Python 用户输入和字符串格式化指南
Python 允许用户输入数据.这意味着我们可以向用户询问输入.在 Python 3.6 中,使用 input() 方法来获取用户输入.在 Python 2.7 中,使用 raw_input() 方法 ...
- 《实现领域驱动设计》笔记——DDD入门
设计不只是感观,设计就是产品的工作方式. 我们的目标应该是创造一个可观测的.可伸缩的.组织良好的软件模型. DDD同时提供了战略上的战术上的建模工具. 我能DDD吗? DDD首先并不是关于技术的,而是 ...
- vue-router钩子执行顺序
Vue的路由在执行跳转时,根据源码可知,调用了router中定义的navigate函数 function push(to: RouteLocationRaw) { return pushWithRed ...
- Python中的爬虫应用及常用Python库
Python的爬虫应用非常广泛,以下是一些典型的示例: 数据采集:使用爬虫可以从网页上抓取数据,并将其保存到本地或数据库中.这对于构建大规模数据集.进行市场调研.舆情监测等任务非常有用. 搜索引擎索引 ...
- STL unordered类容器浅谈
一个代码: #include<cstdio> #include<vector> #include<functional> #include<algorithm ...
- offscreenCanvas+worker+IndexedDB实现无感大量图片缓存
一个有必要实现的需求 因为项目中需要使用canvasTexture(一个threejs3d引擎中的材质类型),绘制大量的图片,每次使用都会请求大量的oss图片资源,虽然重复请求会有磁盘缓存但毕竟这个磁 ...
- 递归与分治思想:治思想 && 折半查找法(迭代 && 递归)
1 //分治思想:将大问题拆成小问题逐一解决 2 //折半查找法:不断缩小一半查找的范围,知道达到目的,效率较高. 详情见:https://fishc.com.cn/thread-27964-1-1. ...
- 4 HTTP的“四层”和“七层”
目录 1 四层:TCP/IP 网络分层模型 2 七层:OSI网络分层模型 3 TCP/IP 协议栈的工作方式 1 四层:TCP/IP 网络分层模型 四层是指TCP/IP 网络分层模型. 第一层:&qu ...
- 流媒体服务器ZLMediaKit与FFmpeg
流媒体服务器ZLMediaKit与FFmpeg overview 关键字:ZLMediaKit.FFmpeg.srt.vlc 如果想快速拥有自己的流媒体服务器,那么可以使用开源项目自己搭建.开源的流媒 ...
- 算法1:Fibonacci数列
斐波那契数列(Fibonacci) 一.背景介绍 斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为 ...