导航
-
- 一. ThreadLocal 简介
- 二. ThreadLocal 源码解析
-
- 1. get
- 2. set
- 3 .remove
- 4. initialValue
- 三. ThreadLocalMap 源码分析
-
- 1. 构造方法
- 2. getEntry()
- 3. set()
- 4. resize()
- 5. expungeStaleEntries()
- 6. cleanSomeSlots()
- 7. nextIndex()
- 8. remove()
- 9. 总结ThreadLocalMap
- 四. 内存泄漏
-
- 1. 产生原理分析
- 2. 解决方式
- 五. ThreadLocal 常见的应用场景
-
- 1. 数据库连接
- 2. 事务管理
- 3. 用户身份认证
- 4. 线程安全的计数器
管网上的介绍是这样:
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently ini alized copy of the variable. ThreadLocal instances are typically private sta c fields in classes that wish to associate state with a thread (e.g., a user ID or Transac on ID).
此类提供线程局部变量。这些变量与正常的对应变量的不同之处在于,每个访问一个变量(通过其get或set方法)的线程都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的私有静态字段,这些字段希望将状态与线程(例如,用户ID或Transac-on ID)相关联。
一. ThreadLocal 简介
ThreadLocal 是 Java 中的一个类,它提供了线程局部变量的机制。线程局部变量是指每个线程都有自己独立的变量副本,线程之间互不影响。
通常情况下,如果多个线程同时访问一个共享变量,需要进行同步来保证线程安全,如Synchronized。而使用 ThreadLocal 也可以避免线程安全问题,因为每个线程都拥有自己的变量副本,且可以独立地操作自己的变量副本,而不会影响其他线程的副本。线程之间的变量副本互不干扰,保证了线程安全。
二. ThreadLocal 源码解析
ThreadLocal 类接口很简单,有4个核心内部成员方法
1. get
返回当前线程所对应的线程局部变量
public T get() {
// 获取当前线程Thread对象
Thread t = Thread.currentThread();
// 获取当前线程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 通过ThreadLocal对象获取变量副本
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
// 返回变量副本
T result = (T)e.value;
return result;
}
}
// 如果ThreadLocalMap为空,或者变量副本未找到,则调用initialValue()方法初始化变量副本
// 返回一个null
return setInitialValue();
}
ThreadLocalMap 虽然是ThreadLocal的静态内部类, 但在Thread类中也有一个这样类型成员变量,也就是说真正实例化ThreadLocalMap是在Thread类中实现的, 所以getMap()方法实际上返回的是Thread类中成员变量
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
2. set
设置当前线程的线程局部变量的值
public void set(T value) {
// 获取当前线程Thread对象
Thread t = Thread.currentThread();
// 通过Thread对象获取ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
if (map != null)
// 如果ThreadLocalMap对象存在,则直接设置变量副本
map.set(this, value);
else
// 如果ThreadLocalMap对象不存在,则创建一个新的ThreadLocalMap对象,并设置变量副本
createMap(t, value);
}
3 .remove
将当前线程局部变量的值删除, 目的是为了减少内存的占用, 避免内存泄漏
该方法是 JDK 5.0 新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收, 所以显式调用该方法清除线程的局部变量并不是必须的操作, 但它可以加快内存回收的速度。
public void remove() {
//获取当前线程的ThreadLocalMap
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
//this指当前ThreadLocal实例
//ThreadLocalMap中的Entry[]数组中,删除Entry[]数组中使用当前ThreadLocal实例作为key的Entry
m.remove(this);
}
4. initialValue
返回该线程局部变量的初始值
该方法是一个 protected 的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用 get() 或 set(Object)时才执行,并且仅执行1次。ThreadLocal 中的缺省实现直接返回一 个null。
protected T initialValue() {
return null;
}
三. ThreadLocalMap 源码分析
每个线程所拥有的变量的副本数是不定的,有些线程可能有一个,有些线程可能有 2个甚至更多, 则线程内部存放变量副本需要一个容器, 而且容器要支持快速存取, 所以在每个线程内部都可以持有一个 Map 来支持多个变量 副本,这个Map被称为ThreadLocalMap。
static class ThreadLocalMap {
/**
* Entry数组,用于存储ThreadLocal与变量副本之间的映射关系
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
//变量副本
Object value;
//key-ThreadLocal实例 value-变量副本
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// 数组的大小必须为 2 的次幂
private static final int INITIAL_CAPACITY = 16;
// Entry对象数组,用于存储ThreadLocal与变量副本之间的映射关系
private Entry[] table;
// 记录当前数组中实际存在元素个数
private int size = 0;
// ThreadLocalMap的阈值,当size超过该值时进行扩容操作