ThreadLocal了解
# ThreadLocal了解
# 原理解析
通过ThreadLocal获取当前线程保存的值的流程:
- 先得到当前线程对象
- 再从当前线程获取ThreadLocalMap
- 最后传入this(即ThreadLocal)获取对应的值
创建ThreadLocal时,里面的属性并未初始化,只有当第一次set时才会创建容器,且装数据的容器也不是放在ThreadLocal里面的,而是在Thread里面,一个是threadLocals,一个是inheritableThreadLocals,(数据结构都是hashMap)ThreaLocal只负责操作数据,并不负责储存
threadLocals:使用ThreadLocal操作(get,set)数据时,数据都是保存在线程的threadLocals中。 inheritableThreadLocals:使用InheritableThreadLocal类操作数据时,数据会保存在inheritableThreadLocals中。
那既然有一个容器threadLocals
了,那为什么还要一个inheritableThreadLocals
呢?
因为ThreadLocal中只会保存本线程操作过的数据,如果子线程想访问父线程,只通过ThreadLocal是不行的,因为他们只属于这两个线程,对应着两个threadLocals
。
所以jdk的开发者们想再在创建一个类似ThreadLocal的类-InheritableThreadLocal
类 不去操作threadLocals
,而是操作inheritableThreadLocals
变量,父线程可以将想要传递的数据放到inheritableThreadLocals
里面,然后子线程初始化的时候还会检查,父线程的inheritableThreadLocals
有没有数据,如果有的话,则加入到子线程的inheritableThreadLocals
中。
THreadLocal使用案例
/**
* threadLocal 使用
* @throws InterruptedException
*/
@Test
public void useCaseThreadLocal() throws InterruptedException {
MyThreadLocal myThreadLocal = new MyThreadLocal();
Thread thread1 = new Thread(() -> {
myThreadLocal.threadLocal.set("thread1 set value");
myThreadLocal.print();
});
Thread thread2 = new Thread(() -> {
myThreadLocal.threadLocal.set("thread2 set value");
myThreadLocal.print();
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
利用InheritableThreadLocal实现父线程传递变量给子线程
/**
* 父线程传递变量到子线程
* @throws InterruptedException
*/
@Test
public void parentTransferDataToChild() throws InterruptedException {
ThreadLocal<String> threadLocal = new InheritableThreadLocal<String>();
threadLocal.set("parent' data");
Thread thread1 = new Thread(() -> {
System.out.println("子线程:"+threadLocal.get());
});
thread1.start();
thread1.join();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# ThreadLocal 内存泄露的原因
首先明白ThreadLocal和TheadLocalMap,Entry的关系
当线程结束时,因为 ThreadLocal标记为WeakReference弱引用,堆里面的ThreadLocal会被下一次GC扫描并回收掉,
但是Entry中的value存在强引用,所以可能会造成内存泄露。
当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
解决办法:
手动释放资源
ExecutorService es;
ThreadLocal tl;
es.execute(()->{
//ThreadLocal 增加变量
tl.set(obj);
try {
// 省略业务逻辑代码
}finally {
// 手动清理 ThreadLocal
tl.remove();
}
});
2
3
4
5
6
7
8
9
10
11
12