一、Thread Local 是什么?
线程本地变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不影响其他线程,做到了线程隔离。
二、Thread Local 的实现原理是什么?
ThreadLocal原理 · 进击的java菜鸟
ThreadLocal有一个静态内部类ThreadLocalMap, ThreadLocalMap又包含一个Entry数组,Entry本身是一个弱引用,它的key是指向ThreadLocal的弱引用,Entry具备了保存key value键值对的能力。弱引用的目的是为了防止内存泄漏,如果是强引用那么ThreadLocal对象除非线程结束否则无法被回收,弱引用则会在下一次GC的时候被回收。
但是这样还是会存在内存泄露的问题,假如key和ThreadLocal对象被回收之后,entry中就存在key为null,但是value有值的entry对象,但是永远没办法被访问到,同样除非线程结束运行。但是只要ThreadLocal使用恰当,在使用完之后调用remove方法删除Entry对象,实际上是不会出现这个问题的。
ThreadLocal 是一个线程的本地变量,也就意味着这个变量是线程独有的,是不能与其他线程共享的,这样就可以避免资源竞争带来的多线程的问题,这种解决多线程的安全问题和lock(这里的lock 指通过synchronized 或者Lock 等实现的锁) 是有本质的区别的:
- lock 的资源是多个线程共享的,所以访问的时候需要加锁。
- ThreadLocal 是每个线程都有一个副本,是不需要加锁的。
- lock 是通过时间换空间的做法。
- ThreadLocal 是典型的通过空间换时间的做法。
三、Thread Local 有什么作用?
ThreadLocal的作用是提供线程内的局部变量,在多线程环境下访问时能保证各个线程内的ThreadLocal变量各自独立。 也就是说,每个线程的ThreadLocal变量是自己专用的,其他线程是访问不到的。
四、Thread Local 如何使用?
Thread Local 的使用方法是通过 ThreadLocal 类创建一个 ThreadLocal 对象,并通过设置和获取方法来操作其中的变量副本。在线程中通过调用 set 方法可以设置当前线程的变量副本的值,在其他地方通过 get 方法可以获取当前线程的变量副本的值。
定义了一个 ThreadLocal 变量 threadLocal
,它的泛型类型是 Integer。然后创建了两个线程,并分别启动它们。每个线程都会执行一个 Runnable 实现类 MyRunnable,在这个类中,使用了 ThreadLocal 来保存每个线程的局部变量值,并在 run 方法中进行设置、获取和清除操作。
通过这个示例,可以看到不同线程之间的局部变量互不干扰,每个线程都可以独立地访问和修改自己的局部变量值,而不会影响其他线程。
public class ThreadLocalExample {// 定义一个 ThreadLocal 变量private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 创建两个线程,并启动Thread thread1 = new Thread(new MyRunnable("Thread-1"));Thread thread2 = new Thread(new MyRunnable("Thread-2"));thread1.start();thread2.start();}static class MyRunnable implements Runnable {private String name;public MyRunnable(String name) {this.name = name;}@Overridepublic void run() {// 设置当前线程的局部变量值threadLocal.set((int) (Math.random() * 100));// 获取当前线程的局部变量值并打印System.out.println("Thread " + name + " ThreadLocal value: " + threadLocal.get());// 清除当前线程的局部变量值threadLocal.remove();}}
}
五、Thread Local 有哪些常见使用场景?
-
线程安全的对象存储: 在多线程环境中,有时候需要每个线程都拥有自己的对象实例,以避免线程安全问题。ThreadLocal 可以用来存储线程私有的对象实例,每个线程都可以独立地访问自己的对象实例,而不会受到其他线程的影响。
-
Web 应用中的用户身份信息传递: 在 Web 应用中,用户的身份信息通常会在多个组件或层之间传递,例如在拦截器、过滤器、Servlet、Spring MVC 控制器等中。使用 ThreadLocal 可以方便地将用户身份信息存储在当前线程中,在整个请求处理过程中都可以方便地获取到用户身份信息,而不必在每个方法参数中传递。
-
数据库连接管理: 在需要频繁地访问数据库的应用中,为了提高性能通常会使用连接池管理数据库连接。ThreadLocal 可以用来存储线程私有的数据库连接,保证每个线程都能够独立地获取自己的数据库连接,而不会出现多线程并发访问数据库连接的问题。
-
事务管理: 在使用事务进行数据库操作时,通常会将事务与当前线程进行绑定,以确保在同一个事务中执行的多个数据库操作能够在同一个事务上下文中进行。ThreadLocal 可以用来存储当前线程的事务上下文,保证每个线程都能够独立地获取自己的事务上下文,而不会影响其他线程的事务操作。
-
线程上下文信息传递: 在某些情况下,需要在线程之间传递一些上下文信息,例如请求标识、日志跟踪信息等。ThreadLocal 可以用来存储线程私有的上下文信息,每个线程都可以独立地获取自己的上下文信息,而不会影响其他线程。
六、Thread Local 有哪些注意事项?
- 内存泄漏:要及时清理不再需要的 Thread Local 变量,避免长时间持有对对象的引用导致内存泄漏。
- 并发安全:尽量避免在多个线程之间共享可变状态的 Thread Local 变量,以确保线程安全。
- 全局状态:过度使用 Thread Local 可能会导致程序变得难以理解和维护,应该谨慎使用,并考虑是否有更好的解决方案。
- 性能影响:大量的 Thread Local 变量可能会增加线程间切换的开销,需要权衡使用场景和性能影响。