ThreadLocal
-
什么是ThreadLocal?
ThreadLocal 是线程本地变量类,在多线程并行执行过程中,将变量存储在ThreadLocal中,每个线程中都有独立的变量,因此不会出现线程安全问题。 -
应用举例
解决线程安全问题:例如每个线程都绑定一个数据库连接,避免多个线程访问同一个数据库连接。比如我们为每一个线程绑定一个SqlSession(核心思想就是为每个线程分配一个资源)。
跨函数参数传递(共享信息):同一个线程,跨类,跨方法传递参数的时候可以使用ThreadLocal。 -
关于ThreadLocal
1)一个线程只能有一个ThreadLocal吗?
首先我们看到Thread里面,有一个ThreadLocalMap,可以看到再java1.8之后,每个线程都有自己的ThreadLocalMap。在JDK1.8之前是一个全局的ThreadLocalMap。
2)ThreadLocalMap 里面可以明显的看到是一个数组用于存储多个Entry
我们看ThreadLocal的源码可以发现是这样一个流程:
1)我们new 一个ThreadLocal 。
2)ThreadLocal.set() 的时候完成了 ThreadLocal 与当前线程的绑定(获取当前线程,然后往当前线程的ThreadLocalMap存放数据)。
3)最终执行的是ThreadLocalMap.set方法。
如果没有初始化, 还需要调用createMap方法(延迟初始化
):
ThreadLocalMap里面的set方法:
关于ThreadLocalMap的哈希探测可以参考此文
这篇文章写的非常的详细,更多的展示了源码的细节
4. Entry 的 Key 为什么需要使用弱引用?如果是强引用会造成什么问题?
因为弱引用在GC回收时会被回收,如果使用强引用,我们某个方法里面创建了一个ThreadLocal,我们肯定使用了new关键字,这个时候是强引用,如果这个方法结束,对于这个方法的强引用解除了。但是在ThreadMap里面还存在引用,如果是软引用则在下一次垃圾回收的时候就会被回收。
如果我们Entry里面使用强引用就会造成内存泄漏,就比如是一个线程池,这个时候线程的生命周期会很长,在上一次执行任务用到的ThreadLocal已经是垃圾了,不会被再使用,但是如果key是强引用导致始终无法被回收掉。
这样的话有资源一直回收不掉,堆积后最终就会OOM内存溢出。所以使用了弱引用进行包装源码如下:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
- 使用案例(持续补充)
1)传递共享参数
// 实现 executeOne 和 executeTwo 方法共享
public class DemoTask {
private ThreadLocal threadLocal;
public void executeOne() {
System.out.println(threadLocal.get().toString());
}
public void executeTwo() {
System.out.println(threadLocal.get().toString());
}
public static void main(String[] args) {
ThreadLocal<String> st = new ThreadLocal<>();
st.set("liyong jiayou");
DemoTask demoTask = new DemoTask();
demoTask.threadLocal = st;
demoTask.executeOne();
demoTask.executeTwo();
}
}