ThreadLocal 是什么?有哪些使用场景?

ThreadLocal 是线程本地存储,在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。

经典的使用场景是为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的 Connection; 还有 Session 管理 等问题。

ThreadLocal 使用例子:

public class TestThreadLocal {//线程本地存储变量private static final ThreadLocal<Integer> THREAD_LOCAL_NUM = new ThreadLocal<Integer>() {@Overrideprotected Integer initialValue() {return 0;}};public static void main(String[] args) {for (int i = 0; i < 3; i++) {//启动三个线程Thread t = new Thread() {@Overridepublic void run() {add10ByThreadLocal();}};t.start();}}/*** 线程本地存储变量加 5*/private static void add10ByThreadLocal() {for (int i = 0; i < 5; i++) {Integer n = THREAD_LOCAL_NUM.get();n += 1;THREAD_LOCAL_NUM.set(n);System.out.println(Thread.currentThread().getName() + " : ThreadLocal num=" + n);}}}

打印结果:启动了 3 个线程,每个线程最后都打印到 "ThreadLocal num=5",而不是 num 一直在累加直到值等于 15

Thread-0 : ThreadLocal num=1
Thread-1 : ThreadLocal num=1
Thread-0 : ThreadLocal num=2
Thread-0 : ThreadLocal num=3
Thread-1 : ThreadLocal num=2
Thread-2 : ThreadLocal num=1
Thread-0 : ThreadLocal num=4
Thread-2 : ThreadLocal num=2
Thread-1 : ThreadLocal num=3
Thread-1 : ThreadLocal num=4
Thread-2 : ThreadLocal num=3
Thread-0 : ThreadLocal num=5
Thread-2 : ThreadLocal num=4
Thread-2 : ThreadLocal num=5
Thread-1 : ThreadLocal num=5

实现原理:

按照我们第一直觉,感觉 ThreadLocal 内部肯定是有个 Map 结构,key 存了 Thread,value 存了 本地变量 V 的值。每次通过 ThreadLocal 对象的 get() 和 set(T value) 方法获取当前线程里存的本地变量、设置当前线程里的本地变量。

而 JDK 的实现里面这个 Map 是属于 Thread,而非属于 ThreadLocal。ThreadLocal 仅是一个代理工具类,内部并不持有任何与线程相关的数据,所有和线程相关的数据都存储在 Thread 里面。ThreadLocalMap 属于 Thread 也更加合理。

还有一个更加深层次的原因,这样设计不容易产生内存泄露。
ThreadLocal 持有的 Map 会持有 Thread 对象的引用,只要 ThreadLocal 对象存在,那么 Map 中的 Thread 对象就永远不会被回收。ThreadLocal 的生命周期往往比线程要长,所以这种设计方案很容易导致内存泄露。

JDK 的实现中 Thread 持有 ThreadLocalMap,而且 ThreadLocalMap 里对 ThreadLocal 的引用还是弱引用(WeakReference),所以只要 Thread 对象可以被回收,那么 ThreadLocalMap 就能被回收。JDK 的这种实现方案复杂但更安全。

在线程池中使用 ThreadLocal 为什么可能导致内存泄露呢?

在线程池中线程的存活时间太长,往往都是和程序同生共死的,这样 Thread 持有的 ThreadLocalMap 一直都不会被回收,再加上 ThreadLocalMap 中的 Entry 对 ThreadLocal 是弱引用(WeakReference),所以只要 ThreadLocal 结束了自己的生命周期是可以被回收掉的。
Entry 中的 Value 是被 Entry 强引用的,即便 value 的生命周期结束了,value 也是无法被回收的,导致内存泄露。

线程池中,如何正确使用 ThreadLocal?

在 finally 代码块中手动清理 ThreadLocal 中的 value,调用 ThreadLocal 的 remove()方法。

ThreadLocal 核心方法

  • 设置 Thread 对应的 Value 值,首次会创建一个 ThreadLocalMap ,添加 ThreadLocal - Value 到 ThreadLocalMap 中,并且绑定 ThreadLocalMap 到当前线程。
public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);
}
  • 创建 ThreadLocalMap,绑定到当前线程。
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}
  • 通过 ThreadLocalMap 获取当前线程的存储的 Value 值
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();
}
  • 设置 ThreadLocal 的初始化值,未 set(T value) 初次获取 Thread 对应的 Value 值时会调用,即被 setInitialValue 方法调用。需要重写该方法。
protected T initialValue() {return null;
}
  • 移除当前线程存储的 Value 值。当 ThreadLocal 不在使用,最好在 finally 语句块中,调用 remove() 方法,释放去 Value 的引用,避免内存泄露。
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);
}

更多细节可查阅 JDK 的 ThreadLocal 和 Thread 源码。


【Java面试题与答案】整理推荐

  • 基础与语法
  • 集合
  • 网络编程
  • 并发编程
  • Web
  • 安全
  • 设计模式
  • 框架
  • 算法与数据结构
  • 异常
  • 文件解析与生成
  • Linux
  • MySQL
  • Oracle
  • Redis
  • Dubbo

ThreadLocal 是什么?有哪些使用场景?相关推荐

  1. 12、Nepxion Discovery 之 Discovery Agent 解决异步场景线程 ThreadLocal 上下文丢失问题

    在进行微服务调用的时候,不管是服务之间(A 服务调用 B 服务)还是服务内部调用(服务 A 某个方法进行里有异步)都存在异步调用.但是 Nepxion Discovery 在进行参数传递的时候很多情况 ...

  2. 全方位,多角度理解ThreadLocal

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:https://blog.csdn.net/zzg1229059735/article/details/82715741 本次 ...

  3. java get请求传参_Java-ThreadLocal三种使用场景

    ThreadLocal对于每一位Java读者而言我想可能都不陌生,因为面试基本都会被问到. Java-ThreadLocal三种使用场景 关于ThreadLocal JDK1.2的版本中就提供java ...

  4. Java-ThreadLocal三种使用场景

    关于ThreadLocal JDK1.2的版本中就提供java.lang.ThreadLocal类,每一个ThreadLocal能够放一个线程级别的变量, 它本身能够被多个线程共享使用,并且又能够达到 ...

  5. ThreadLocal怎么实现线程隔离的?可见性问题?为什么要重新定义一个threadLocalHashCode?为什么有内存泄露?弱引用又是什么?

    1. ThreadLocal实现线程隔离的使用场景 线程1的if代码块要执行的话,那么flag.get()的值必须是false:同理线程2的if代码块要执行的话,flag.get()的值必须是true ...

  6. j2ee servlet 和 threadlocal ,synchronized 与 web容器

    在传统的Web开发中,我们处理Http请求最常用的方式是通过实现Servlet对象来进行Http请求的响应.Servlet是J2EE的重要标准之一,规定了Java如何响应Http请求的规范.通过Htt ...

  7. 浅析ThreadLocal

    线程本地变量 每个线程独立拥有,线程存在,ThreadLocal就一直存在. 使用场景:spring 事务 里面有m方法,m调用了m1,m2,比如m1,m2都去访问数据库 如果分别建立两个数据库连接, ...

  8. java线程间ThreadLocal的传递

    文章目录 1 场景 2 需确认问题 2.1 继承线程的ThreadLocal的含义 2.2 子线程内的ThreadLocal的值和父线程内的有什么关系? 2.3 父线程内的ThreadLocal清除后 ...

  9. JAVA基础系列:ThreadLocal

    1. 思路 什么是ThreadLocal? ThreadLocal类顾名思义可以理解为线程本地变量.也就是说如果定义了一个ThreadLocal,每个线程往这个ThreadLocal中读写是线程隔离, ...

  10. 我对ThreadLocal的一些理解 内存泄露啥的

    1. 什么是ThreadLocal ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔 ...

最新文章

  1. Saltstack系列之一——安装篇
  2. linux shell 计算时间差
  3. Kubernetes v1.6开始支持RBAC
  4. 好久没有深入研究技术了,最近这两年太忙但又不知道忙了些什么
  5. Alpha冲刺第二天
  6. sqlserver一些对象的创建
  7. c++ 智能指针auto_ptr (c++98)、shared_ptr(c++ 11)、unique_ptr(c++ 11)、weak_ptr(c++ 11)
  8. Pwn入门之ELF文件
  9. 网络工具之PacketTracer8安装
  10. 免费开放的电子图书馆
  11. 台式电脑怎么组装步骤_台式机组装教程,详细教您台式机怎么组装
  12. qq说说时间轴php实现,QQ说说时间 qq说说时间轴
  13. CSS3 低多边形(Low Poly)设计及开发实例 - 简单的字母
  14. RS-485详解(一)
  15. Ajax与分页的实现
  16. cwd命令linux,PCMan's FTP Server 'CWD'命令缓冲区溢出漏洞
  17. Yocto系列讲解[入门篇] 1 - 快速入门熟悉Yocto的构建
  18. python-test1
  19. Oracle 使用序列创建自增字段
  20. Fortify SCA安装以及卸载

热门文章

  1. 训练日记2019.11.11 莫队求区间众数
  2. 【认知实习】虚拟现实体验
  3. 合服 两个服务器都有什么作用,阴阳师合服是什么意思?合服合区问题汇总详解[多图]...
  4. 【产业互联网周报】外媒:英特尔等公司暂停向俄罗斯发货;阿里云季度营收195亿元;第四范式再次提交上市申请...
  5. Mysql组复制(MGR)——操作
  6. 数值计算——线性最小二乘问题
  7. matlab 椭圆方程拟合
  8. S3C2440移植linux3.4.2内核之内核框架介绍及简单修改
  9. 奇迹网站系统IGC奇迹mu S18网站可视化装备模板
  10. dnf剑魂buff等级上限_DNF95全新版本,剑魂之玩法攻略