引言:
ThreadLocal是每个线程私有的存储数据的地方,在使用它时,可能会产生内存泄漏问题,本文将从带你认识ThreadLocal 开始,分析造成问题的原因,并给出解决问题的方法.
1.认识ThreadLocal
1.1 ThreadLocal是什么
ThreadLocal是JDK自带的一个类,它并不是一个线程,有些人称呼他为线程变量,该变量对其他线程而言是隔离的,也就是说对每个线程来说它是私有的.我们可以将简单的理解为每个线程单独存放数据的地方.
1.2 ThreadLocal的作用
ThreadLocal是解决线程并发问题的一种思路,前面介绍过ThreadLocal变量是每个线程私有的,ThreadLocal变量就相当于是被static修饰一样,使用ThreadLocal变量,我们可以保证在同一个线程可以访问到同一个数据对象.
1.3 ThreadLocal的使用
代码演示:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class ThreadLocalDemoTest {
public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
static void print(String str) {
//打印当前线程中本地内存中本地变量的值
System.out.println(str + " :" + threadLocal.get());
//清除本地内存中的本地变量
threadLocal.remove();
}
@Test
public void test() {
new Thread(() -> {
ThreadLocalDemoTest.threadLocal.set("localA");
print("A");
//打印本地变量
System.out.println("after remove : " + threadLocal.get());
}, "A").start();
new Thread(() -> {
ThreadLocalDemoTest.threadLocal.set("localB");
print("B");
System.out.println("after remove : " + threadLocal.get());
}, "B").start();
}
}
从这个示例中我们可以看到,两个线程分表获取了自己线程存放的变量,他们之间变量的获取并不会错乱。
2.ThreadLocal内存泄露问题
首先我们的知道,ThreadLocal的se方法实际是创建了一个ThreadLocalMap的内部变量,我们可以将ThreadLocalMap 理解为一个定制化的HashMap,而ThreadLocalMap的key 是一个弱引用,如果外部没有强引用指向它,它就会被GC回收,导致Entry的Key为null,而Entry的value却是一个强引用,如果我们不采取任何措施,value无法被回收,这时内存泄漏就发生了,value成了一个永远也无法被访问,但是又无法被回收的对象. 这就是ThreadLocal发生内存泄露问题的原因.
3.ThreadLocal内存泄露问题解决方法
我们知道造成内存泄漏问题的主要原因,说白了其是就是value没被回收导致的,我们只要采取一个措施,使vuale能被回收就可以解决内存泄漏问题.其实ThreadLocal本身已经考虑到了这个问题,我们只要按照规范,使用完ThreadLocal方法后,手动调用remove()方法,会将key为空的键值对清空,就解决了ThreadLocal可能会产生的内存泄漏问题.