ThreadLocal —— 线程变量
作用
创建一个在不同线程之间读写相互隔离的变量。
大白话:同一个变量,在不同线程的环境下,访问和修改它的值都互不影响,不同线程允许拥有不同的值。
用法
声明为一个静态的类属性。
以存储 User 信息为例:
提供方法
- saveUser 设置 User 信息
- getUser 获取 User 信息
- removeUser 移除 User 信息
1 | public class UserHolder { |
原理
ThreadLocal 对象内部的数据是由 ThreadLocalMap 维护的,
而每个 Thread 又有各自的 ThreadLocalMap。
因此“表面上”线程共享的类属性变量,实际上在每个 Thread 中读写的对象并不是同一个。
假设这有两个线程 A,B 操作一个线程变量 tl。
在 A 线程中 tl 的值是 1。
如果 B 线程想要修改它的值,就需要先找到当前 Thread 的 ThreadLocalMap,然后修改 map 中 tl 这个 key 所匹配的 value。
什么叫做维护呢?
这里以上述的 tl 表示 ThreadLocal 实例,并贴出该类的源码。
set
当 tl 进行 set 操作时,以自身实例作为 key,存储对象作为 value,存入了 ThreadLocalMap 中。
1 | public void set(T value) { |
get
同理进行 get 操作时,以自身实例作为 key,索引 ThreadLocalMap 中的值。
1 | public T get() { |
remove
remove 操作也是如此,以自身实例为 key,移除这一对键值对。
1 | public void remove() { |
本质
并不是 ThreadLocal 为每个线程提供了不同的变量副本,而是其借助不同线程中的 ThreadLocalMap 存储了当前线程的变量副本。
内存泄漏
内存泄漏:指程序中已分配的内存未能成功释放,导致可用内存逐渐减少的现象。
为什么线程变量通常声明为一个静态类属性,而不是局部变量呢?
因为 垃圾回收 机制。
如果将 ThreadLocal 实例声明为一个局部变量,在作用域结束时,会回收掉这个变量引用,导致 ThreadLocalMap 中存储了一个 key 为 null 的键值对,无法进行访问,也不能主动销毁。
从而发生内存泄漏。
疑问:question:
虽然引用被回收了,但是内存空间不会被释放吧,key 不至于为 null 吧?因为 map 的 key 保存了这个实例?
答:
Map 的 keys 保留了这个实例对象,所以这个 key 并不是 null;
只不过丢失了对这个 key 的引用,无法主动的查询对应的值,但是可以通过 map 的 keys,一一返回 value。
所以上述解释 “导致 ThreadLocalMap 中存储了一个 key 为 null 的键值对” 改为:“导致 ThreadLocalMap 中存储了一个“消失的” key 的键值对”
上述解释仍存疑:question:
弱引用?和垃圾回收的关系?