您现在的位置是:亿华云 > 数据库

为什么 ThreadLocal 可以做到线程隔离?

亿华云2025-10-02 18:43:39【数据库】7人已围观

简介​对于 ThreadLocal 我们都不陌生,它的作用如同它的名字——用于存放「线程本地」变量。先通过一个小例子感受一下:private static final ThreadLocalthreadL

​对于 ThreadLocal 我们都不陌生,线程它的隔离作用如同它的名字——用于存放「线程本地」变量。

先通过一个小例子感受一下:

为什么 ThreadLocal 可以做到线程隔离?

private static final ThreadLocalthreadLocal = new ThreadLocal<>();

为什么 ThreadLocal 可以做到线程隔离?

public static void main(String[] args) throws Throwable {

为什么 ThreadLocal 可以做到线程隔离?

Thread threadOne = new Thread(()->{

threadLocal.set("ThreadOne:" + Thread.currentThread().getName());

log.info("线程 One 本地变量值为:{ }",线程 threadLocal.get());

threadLocal.remove();

log.info("线程 One remove 后本地变量值为:{ }", threadLocal.get());

});

Thread threadTwo = new Thread(()->{

threadLocal.set("ThreadTwo:" + Thread.currentThread().getName());

log.info("线程 Two 本地变量值为:{ }", threadLocal.get());

});

threadOne.start();

threadTwo.start();

}

运行结果:

线程 One 本地变量值为:ThreadOne:Thread-0

线程 One remove 后本地变量值为:null

线程 Two 本地变量值为:ThreadTwo:Thread-1

OK,从效果上看,隔离ThreadLocal 确实是线程线程隔离的,那么,隔离它是线程如何做到线程隔离的呢?下面我们扒一扒源码,看看它是隔离如何做到的:

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() 方法的逻辑如下:

获取当前线程根据当前线程获取一个 ThreadLocalMap 对象如果 map 不为 null 则保存如果 map 为 null 则创建一个 map

getMap() 和 createMap() 方法都干了啥呢?我们点进去看:

ThreadLocalMap getMap(Thread t) {

return t.threadLocals;

}

void createMap(Thread t, T firstValue) {

t.threadLocals = new ThreadLocalMap(this, firstValue);

}

进入到两个方法内部后发现,不管执行哪个分支,线程最终是隔离把值保存到了当前线程的 threadLocals 属性中。

查看 Thread 类的线程源码,亿华云计算你会发现类中定义了一个 threadLocals 属性,隔离且初始值为 null,线程其类型为ThreadLocal.ThreadLocalMap。隔离

public class Thread implements Runnable {

// ...

ThreadLocal.ThreadLocalMap threadLocals = null;

// ...

}

到此,线程我们发现了,原来 ThreadLocal 就是把我们要传递的对象放到了当前线程的 threadLocals 属性中。也就是说每个线程在用 ThreadLocal 保存对象时,其实就是将对象放到了当前线程实例对象的 threadLocals 属性里面。这样一来线程之间自然就是互相独立的啦。

再看看 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();

}

private T setInitialValue() {

T value = initialValue();

Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t);

if (map != null)

map.set(this, value);

else

createMap(t, value);

return value;

}

ThreadLocal 的 get() 方法其实和 set() 方法逻辑很相似,先从当前线程的 threadLocals 属性中取,如果该属性为 null,高防服务器那么就初始化。

当线程结束时,会调用当前线程实例的 exit() 方法,将 threadLocals 设置为 null,以便垃圾回收器将其回收掉。

// THread 类中的方法

private void exit() {

// ...

threadLocals = null;

// ...

}

最后,有一点需要格外注意:用完 ThreadLocal 一定要记得手动调用 remove() 方法,否则可能会产生脏数据甚至产生内存泄漏。

为啥呢?上面不是说线程结束时,会将 threadLocals 置为 null 吗?

是的,线程结束时,确实会做清理工作。

但,如果线程一直不结束呢?如果线程会被复用呢?比如使用了线程池。

所以,使用 ThreadLocal 一定要手动 remove()。​

源码下载

很赞哦!(74423)