聊聊 ThreadLocal 类(二)

71 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 11 天,点击查看活动详情

上一篇文章 # 聊聊 ThreadLocal 类(一) ,我们简单看了下 ThreadLocal 在多线程环境下实现共享变量的方式。那么 ThreadLocal 是如何实现每个线程均拥有共享变量的副本,对共享变量的读取和写入互不影响的呢?本篇文章,我们从源码的角度一起来揭秘吧!

ThreadLocal 的原理

之前我们有分析过 Thread 类的源码,Thread 类中有两个成员变量:

public class Thread implements Runnable { 
    // 省略部分代码
    ThreadLocal.ThreadLocalMap threadLocals = null; 
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    // 省略部分代码
}

它们的类型都是 ThreadLocal.ThreadLocalMap 类型,且二者的初始值都为null只有当前线程第一次调用 ThreadLocal 的 set() 方法或者 get() 方法时才会实例化这两个变量。接下来,我们来看看 ThreadLocal 的 set() 方法和 get() 方法的实现。

set 方法

先来看看 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);
}
  • 首先会获取当前线程
  • 以当前线程为Key,获取 ThreadLocalMap 对象
  • 获取的 ThreadLocalMap 对象不为空,设置 value 的值
  • 若获取的 ThreadLocalMap 对象为空,则创建 Thread 类中的 threadLocals 变量
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
  • 获取 ThreadLocalMap 对象,实际上就是获取 Thread 对象的 threadLocals 成员变量
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
  • 获取 ThreadLocalMap 对象为 null 时,就会给 Thread 的成员变量 threadLocals 进行实例化

get 方法

再来看看 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();
}
  • 首先也是先获取当前线程
  • 以当前线程为Key,获取 ThreadLocalMap 对象
  • 如果 ThreadLocalMap 对象不为 null,则返回本地变量对应的值
  • 如果 ThreadLocalMap 对象为 null,则初始化 threadLocals 成员变量的值

remove 方法

remove 方法的作用是删除 ThreadLocalMap 中的 value 资源它的源码比较简单:

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}
  • 根据当前线程获取threadLocals成员变量
  • 当 threadLocals 成员变量不为空,则移除 value 值

注意: 如果调用线程一直不终止,则本地变量会一直存放在调用线程的threadLocals成员变量中,所以,如果不需要再使用本地变量时,可以通过调用ThreadLocal的remove()方法,将本地变量从当前线程的threadLocals成员变量中删除,以免出现内存溢出的问题。