1、ThreadLocal 作用
线程隔离:因为他的 key 是当前线程,所以不同线程 key 不同
线程安全:因为线程隔离,所以线程安全
2、使用介绍
就三个方法:set、get、remove
public class Test {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
threadLocal.set("hello world");
threadLocal.get();
threadLocal.remove();
}
});
}
}
3、remove 方法
3.1 不使用 remove 方法
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
ExecutorService a = Executors.newFixedThreadPool(2);
a.submit(() -> {
for (int b=1;b<3;b++){
System.out.println("线程名:"+Thread.currentThread().getName());
System.out.println("初始值:"+threadLocal.get());
threadLocal.set("hello world");
System.out.println("修改后的值:"+threadLocal.get());
System.out.println("-----------------------------------------");
// threadLocal.remove();
}
});
}
}
打印如下:
线程名:pool-1-thread-1
初始值:null
修改后的值:hello world
-----------------------------------------
线程名:pool-1-thread-1
初始值:hello world
修改后的值:hello world
-----------------------------------------
3.2 使用 remove 方法
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
ExecutorService a = Executors.newFixedThreadPool(2);
a.submit(() -> {
for (int b=1;b<3;b++){
System.out.println("线程名:"+Thread.currentThread().getName());
System.out.println("初始值:"+threadLocal.get());
threadLocal.set("hello world");
System.out.println("修改后的值:"+threadLocal.get());
System.out.println("-----------------------------------------");
// threadLocal.remove();
}
});
}
}
打印如下:
线程名:pool-1-thread-1
初始值:null
修改后的值:hello world
-----------------------------------------
线程名:pool-1-thread-1
初始值:null
修改后的值:hello world
-----------------------------------------
3.3 总结
不使用remove方法后续相同线程依然能获取之前set的值,因此方法介绍后一定要调用 remove 方法
4、底层原理
ThreadLocal 底层是通过 ThreadLocalMap
来实现的,每个Thread对象(注意不是ThreadLocal对象) 中都存在一个ThreadLocalMap, Map的key为ThreadLocal对象, Map的value为需要缓存的值
如上所示,ThreadLocal 里面其实就是一个 ThreadLocalMap,然后这个ThreadLocalMap里面是一个个entry对象,每个entry对象可以理解成存储的不同线程信息,entry里面的key存的就是一个ThreadLocal 对象,value就是你set进去的值。
5、使用注意事项
- 内存泄漏:由于ThreadLocal的生命周期和线程的生命周期不同,如果不注意及时清理ThreadLocal变量,可能会导致内存泄漏。因此,在不再需要使用ThreadLocal时,应调用remove()方法将其从当前线程中清除,避免线程结束后仍然持有对该变量的引用。
- 共享变量问题:尽管ThreadLocal为每个线程提供了独立的变量副本,但它并不能解决线程间共享变量的同步问题。如果多个线程共享同一个ThreadLocal变量,需要自行处理线程间的同步操作,确保线程安全。
- 内部使用慎重:在一些特定的情况下,如使用线程池或者异步任务执行框架,使用ThreadLocal需要格外小心。因为线程池中的线程可能会被复用,如果不正确地处理ThreadLocal变量,可能会导致数据混乱。
6、使用场景介绍
1、线程上下文传递:可以用于同一个线程参数传递,当镶嵌很多方法的时候不需要每一层都传递。
2、线程隔离安全:可以用于多线程并发但是每个线程独有的数据存储。
适合使用ThreadLocal的场景包括但不限于:Web应用中的用户身份验证、数据库连接管理、线程上下文传递等。在这些场景下,可以使用ThreadLocal轻松地在多线程环境下共享数据,提高并发性能和代码可维护性。