问题:讲一下 ThreadLocal面试回答核心话术(可直接用于面试)“ThreadLocal 是 Java 提供的一种线程封闭机制,每个线程都持有一个独立的变量副本,互不干扰,从而实现无锁的线程安全。原理上,每个 Thread 内部维护一个ThreadLocalMap,key 是ThreadLocal对象的弱引用,value 是线程私有的变量副本。调用get()时,先获取当前线程,再拿到当前线程的ThreadLocalMap,以当前ThreadLocal实例为 key 查找对应的 value。因为每个线程只能访问自己的 Map,自然不会有并发冲突。在银行系统中,我主要用它解决两类问题:一是全局信息透传,比如在交易链路中把 traceId、客户号、机构号等塞进 ThreadLocal,这样底层 DAO 或工具类在任何地方都能取到上下文,不用层层传参;二是非线程安全对象的复用,比如SimpleDateFormat在多线程下会有问题,我就用 ThreadLocal 给每个线程单独分配一个实例。内存泄漏是 ThreadLocal 最经典的坑。原因是 ThreadLocal 对象本身被ThreadLocalMap的 Entry 用弱引用包裹,一旦外部的强引用消失,ThreadLocal 会被 GC,但 Entry 中的 value 是强引用,只要线程还活着,value 就不会释放,造成内存泄漏。解决方法是用完之后必须调用remove()。尤其是在线程池场景下,线程复用后如果不清理,会读到上一个任务的脏数据,这在资金交易中可能会引发严重事故。我踩过的坑:有一次线上批量扣款,使用的是线程池,第一个任务的客户号放在 ThreadLocal 里,任务结束后没有 remove。线程被复用执行第二个任务时,直接从 ThreadLocal 里读到了上一个客户号,把 A 的钱扣到了 B 的头上。后来我们规范了:凡是使用 ThreadLocal,必须在 finally 块里 remove,并配合阿里的 TransmittableThreadLocal 解决父子线程传递问题。”详细解析一、ThreadLocal 的数据结构Thread 对象 └── threadLocals (ThreadLocalMap) ├── Entry → key: ThreadLocal@123 (弱引用) value: "traceId=abc123" ├── Entry → key: ThreadLocal@456 (弱引用) value: sdf (SimpleDateFormat) └── ...每个Thread都持有一个ThreadLocalMap。ThreadLocalMap是一个自定义的哈希表,解决冲突用的是开放地址法。Entry继承了WeakReferenceThreadLocal?,key 是弱引用,value 是强引用。二、核心方法源码级理解1.get()publicTget(){Threadt=Thread.currentThread();ThreadLocalMapmap=getMap(t);// 拿到当前线程的 Mapif(map!=null){ThreadLocalMap.Entrye=map.getEntry(this);// 以 this 为 key 查找if(e!=null){return(T)e.value;}}returnsetInitialValue();// 没找到则初始化(调用 initialValue())}2.set()publicvoidset(Tvalue){Threadt=Thread.