反问面试官3个ThreadLocal的问题

简介: 接下来,我想先说说ThreadLocal的用法和使用场景,然后反问面试官3个关于ThreadLocal的话题。

ThreadLocal,一个Java人面试绕不开的话题,我也很奇怪为什么那些面试官很喜欢问这个,也不知道他们自己有没有搞清楚。

接下来,我想先说说ThreadLocal的用法和使用场景,然后反问面试官3个关于ThreadLocal的话题

使用方法和场景

一句话总结:ThreadLocal是给每个线程准备一份“独立的小空间”,它让每个线程都拥有自己独立的变量副本。在多个线程并发访问时,不用担心变量之间的冲突问题,避免了多线程之间的数据共享风险。

使用场景

ThreadLocal的使用场景主要在多线程环境中,能够为每个线程提供独立的变量副本。比如:

  • 用户上下文信息:比如,在Web应用中,每个请求可能由不同的线程处理,在处理用户请求时为每个线程维护独立的用户信息。
  • 数据库连接管理:比如,在多线程环境下,每个线程需要有自己独立的数据库连接。
  • 事务管理:比如,在处理事务时,每个线程可能需要有自己的事务上下文,确保线程安全的事务操作。
  • 数据传递:比如,同一个线程,在不同的方法之间传递数据,但又不想使用方法参数去传递,就可以使用ThreadLocal。像我们常用的日志跟踪场景,跟踪的ID会存在ThreadLocal中贯穿整个链条。

总之有2个场景:

  1. 在多线程场景下,每个线程需要独立管理变量的场景。
  2. 某个线程想在整条链路上共享独立变量的场景。

使用方法

使用时,记住3条核心原则:

  • 每个线程都有一份独立的数据。
  • 线程内部使用的是ThreadLocalMap来保存数据,Key就是ThreadLocal对象。
  • 使用完毕后,记得调用remove方法,防止内存溢出。

代码示例

独立保存变量的示例:

public class ThreadLocal4Independent {

    private static ThreadLocal<Integer> threadLocalVar = new ThreadLocal<>();

    public static void main(String[] args) {
        Runnable task = () -> {
            int num = (int) (Math.random() * 100);
            threadLocalVar.set(num);
            System.out.println("线程:" + Thread.currentThread().getName() + "的值:" + threadLocalVar.get());
            threadLocalVar.remove();
        };

        new Thread(task, "1").start();
        new Thread(task, "2").start();
    }

}

传递参数的示例:

public class ThreadLocal4DataPass {
    // 使用ThreadLocal来存储需要在多个方法间传递的数据
    private static final ThreadLocal<String> threadLocalData = new ThreadLocal<>();

    public static void main(String[] args) {
        // 在主线程中设置数据
        threadLocalData.set("ThreadLocal");

        // 在主线程中调用不同的方法
        method1();
        method2();

        // 清除ThreadLocal变量,防止内存泄露
        threadLocalData.remove();
    }

    private static void method1() {
        // 在method1中获取数据并打印
        String data = threadLocalData.get();
        System.out.println("方法1拿到的数据是:" + data);
    }

    private static void method2() {
        // 在method2中获取数据并打印
        String data = threadLocalData.get();
        System.out.println("方法2拿到的数据是:" + data);
    }
}

聊完使用场景和方法,接下来问面试官几个问题。

问题1:请画出ThreadLocal和Thread的关系图

ThreadLocal和Thread的关系图如下。

这里要牢记3点

  1. 数据实际上是存在ThreadLocalMap中的,ThreadLocalMap归Thread所持有。见源代码。

  2. ThreadLocalMap内部使用的是K-V结构,Key是我们定义的ThreadLocal对象。见源代码。

  3. ThreadLocalMap对ThreadLocal是弱引用关系。见源代码。

问题2:为什么ThreadLocalMap里的Key是弱引用

那为什么ThreadLocalMap里的Key是使用ThreadLocal呢?为什么又是弱引用呢?

这就不得不说JDK的设计者的思想非常精妙了,有3点妙处:

  1. 一个线程要是存了多种数据,总得有个规则去找他们,那就根据定义的ThreadLocal对象去找吧。
  2. 对于开发者来说,他只需要使用ThreadLocal去保存数据即可,无需关系底层结构。也就是说对外暴露简单的使用方式即可,对于不需要调用方知道的细节全部隐藏。
  3. 一般情况下Thread的生命周期会很长,比如Web容器启动后,就会启动大量的线程丢到线程池中复用。所以ThreadLocalMap的生命周期也会很长。但是,ThreadLocal对象存在的周期不一定长,如果,ThreadLocalMap的Key对ThreadLocal是强引用的话,那么ThreadLocal对象就会一直存在于内存中得不到释放,最终会导致内存溢出,所以采用了弱引用。

问题3:为什么ThreadLocal使用不当会造成内存溢出

从上图的关系图可以看出,Value的生命周期是跟着Thread的生命周期来的,如果一直不处理的话,也会出现内存溢出的情况。

为了避免内存溢出的情况,我们在使用完ThreadLocal后,要即使调用remove方法,以便JVM回收Value。

总结

ThreadLocal 是并发编程中的强大工具,能够为每个线程提供独立的变量副本,避免线程安全问题。并且这个ThreadLocal存入的值能够贯穿整个流程。使用时要注意上文的几点,防止造成内存溢出。

本篇完结!欢迎 关注、交流、全网可搜(程序员半支烟)

原文链接:https://mp.weixin.qq.com/s/Z3x_tw1jks_KYE0ljETp0Q

相关文章
|
6月前
|
存储 Java 中间件
《吊打面试官系列》从源码全面解析 ThreadLocal 关键字的来龙去脉
《吊打面试官系列》从源码全面解析 ThreadLocal 关键字的来龙去脉
|
6月前
|
存储 安全 Java
面试题:用过ThreadLocal吗?ThreadLocal是在哪个包下的?看过ThreadLocal源码吗?讲一下ThreadLocal的get和put是怎么实现的?
字节面试题:用过ThreadLocal吗?ThreadLocal是在哪个包下的?看过ThreadLocal源码吗?讲一下ThreadLocal的get和put是怎么实现的?
78 0
|
4月前
|
存储 缓存 Java
Java面试题:解释Java中的内存屏障的作用,解释Java中的线程局部变量(ThreadLocal)的作用和使用场景,解释Java中的锁优化,并讨论乐观锁和悲观锁的区别
Java面试题:解释Java中的内存屏障的作用,解释Java中的线程局部变量(ThreadLocal)的作用和使用场景,解释Java中的锁优化,并讨论乐观锁和悲观锁的区别
51 0
|
4月前
|
并行计算 算法 安全
Java面试题:解释Java内存模型的内存屏障,并讨论其对多线程并发的影响,解释Java中的线程局部变量(ThreadLocal)的工作原理,解释Java中的ForkJoinPool的工作原理
Java面试题:解释Java内存模型的内存屏障,并讨论其对多线程并发的影响,解释Java中的线程局部变量(ThreadLocal)的工作原理,解释Java中的ForkJoinPool的工作原理
42 0
|
4月前
|
Java 数据库连接
Java面试题:Java内存模型中的happens-before关系,Java中的ThreadLocal是如何工作的?Java中的CountDownLatch和CyclicBarrier的区别?
Java面试题:Java内存模型中的happens-before关系,Java中的ThreadLocal是如何工作的?Java中的CountDownLatch和CyclicBarrier的区别?
37 0
|
5月前
|
存储 Java 容器
研二学妹面试字节,竟倒在了ThreadLocal上,这是不要应届生还是不要女生啊?
【6月更文挑战第1天】研二学妹面试字节,竟倒在了ThreadLocal上,这是不要应届生还是不要女生啊?
47 5
|
5月前
|
存储 Java 数据库连接
来探讨一下最近面试问的ThreadLocal问题
来探讨一下最近面试问的ThreadLocal问题
|
6月前
|
存储 缓存 安全
面试被问ThreadLocal要怎么回答?
ThreadLocal是Java中为每个线程提供独立变量副本的类,避免多线程同步。它内部维护ThreadLocalMap,存在内存泄漏风险,使用后需调用remove()。常用于记录用户请求数据、事务处理、日志记录和连接池管理等场景。注意内存泄漏和性能影响,以及与InheritableThreadLocal和同步代码块的区别。【5月更文挑战第7天】
|
6月前
|
存储 Java
面试高频 ThreadLocal类详解
面试高频 ThreadLocal类详解
37 0
|
安全 Java 数据库连接
常见面试题梳理:源码角度彻底揭秘ThreadLocal
ThreadLocal在日常开发中还是比较常见的,本文将从源码的角度彻底揭秘ThreadLocal,并会分享一些较为常见的面试题,let's go。 ThreadLocal是什么? ThreadLocal隶属于lang包,它的主要功能是为每个线程提供一个私有的局部变量,这个变量在线程间相互隔离,互不影响。