每次背面试题,老是记不住ThreadLocal相关的知识,还是没有熟练掌握的原因,这里总结了几个大佬的知识,写下自己的理解。
为什么要用到ThreadLocal?
线程A和线程B都要用到变量C,但是线程A不小心修改了变量C,那线程B就傻眼了,所以出于线程安全的问题,这地方是不需要将变量C进行共享的,那怎么办?假如每个线程中都有一个变量C,并且各个线程之间对变量C的修改访问是互不影响的,即线程隔离。
而我们的ThreadLocal就是解决这个问题的,在代码中任何一处声名的ThreadLocal都可以实现线程隔离的变量。
ThreadLocal简单用法:
String localVar = "123";
ThreadLocal<String> threadLocalObject = new ThreadLocal<String>();
threadLocalObject .set(localVar);
String str = threadLocalObject .get();
ThreadLocal相当于一个工具类,将我们线程要用的变量通过set方法存在线程中,通过get方法拿到变量的值。
那么ThreadLocal是怎么存储的呢?
实际上并不是ThreadLocal存储的,每个Thread都有一个ThreadLocalMap类型的threadlocals成员变量,它存储了本线程中所有的threadlocal对象和对应的值,而ThreadLocalMap是由一个个Entry构建的,key是ThreadLocal对象,value是ThreadLocal对象对应的值。entry是一个弱引用。
这么说起来有点抽象,上图:
其中两个线程虽然操作的是同一个ThreadLocal对象,但是他们访问修改是互不影响的
private static ThreadLocal<String> sThreadLocal = new ThreadLocal<>();
private static ThreadLocal<String> sThreadLoca2 = new ThreadLocal<>();
Stirng str = "测试相同字符串"
public static void main(String args[]) {
sThreadLocal.set("这是在主线程中");
System.out.println("线程名字:" + Thread.currentThread().getName() + "---" + sThreadLoca2.get());
System.out.println("线程名字:" + Thread.currentThread().getName() + "---" + sThreadLocal.get());
//线程a
new Thread(new Runnable() {
@Override
public void run() {
sThreadLocal.set("这是在线程a中");
System.out.println("线程名字:" + Thread.currentThread().getName() + "---" + sThreadLoca2.get());
System.out.println("线程名字:" + Thread.currentThread().getName() + "---" + sThreadLocal.get());
}
}, "线程a").start();
线程名字:main---测试相同字符串
线程名字:main---这是在主线程中
线程名字:线程a---测试相同字符串
线程名字:线程a---这是在线程a中
上面代码相当于下图:
一个ThreadLocal对应着一个变量,如果我们想存多个变量,就多创建多个ThreadLocal,实际上是放在map当中,不用我们担心,不过太多ThreadLocal对象,会导致内存泄露问题,不过这个Entry是个弱引用,也不用我们担心。
关于ThreadLocal内存泄露:(简单谈一下)
由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄露,而不是因为弱引用。跟强弱引用根本没有关系。
引用某位大佬的一段话:
以上为自己的理解,欢迎各位大佬来指点,谢谢
参考文章:
到底为什么我们需要ThreadLocal?_为什么要使用threadlocal-CSDN博客
Thread ThreadLocal和ThreadLocalMap,用法+原理,我懵圈了? - 知乎 (zhihu.com)