1. 是什么ThreadLocal 是线程本地变量给每个线程提供独立的变量副本互不影响。2. 简单用法publicclassUserContext{privatestaticfinalThreadLocalStringUSERnewThreadLocal();publicstaticvoidset(Stringuser){USER.set(user);}publicstaticStringget(){returnUSER.get();}publicstaticvoidremove(){USER.remove();}}// 使用UserContext.set(张三);StringuserUserContext.get();// 同一个线程里拿到的都是张三UserContext.remove();3. 常见场景用户信息传递不用每次方法参数传递// 登录时存进去UserContext.set(SecurityContext.getUser());// 任何地方都能取到publicvoidanyMethod(){UseruserUserContext.get();// 拿到当前登录用户}数据库连接保证同一线程用的是同一个连接privatestaticfinalThreadLocalConnectionDBnewThreadLocal();// 获取连接publicConnectiongetConnection(){ConnectionconnDB.get();if(connnull){conndataSource.getConnection();DB.set(conn);}returnconn;}请求TracerprivatestaticfinalThreadLocalStringTRACE_IDnewThreadLocal();publicvoidhandleRequest(){TRACE_ID.set(UUID.randomUUID().toString());log.info(开始处理, traceId{},TRACE_ID.get());// 整个调用链都能拿到同一个 traceId}4. 原理每个 Thread 对象里有个 ThreadLocalMapThread └── threadLocals: ThreadLocalMap ├── ThreadLocal? → value ├── ThreadLocal? → value └── ...set 时把 ThreadLocal 对象作为 key存到当前线程的 Map 里。get 时从当前线程的 Map 里用 ThreadLocal 对象作为 key 取值。所以不同线程访问同一个 ThreadLocal拿到的值不一样。5. 内存泄漏ThreadLocalMap 的 Entry 继承 WeakReferencestaticclassEntryextendsWeakReferenceThreadLocal?{Objectvalue;Entry(ThreadLocal?k,Objectv){super(k);valuev;}}keyThreadLocal 对象是弱引用理论上会被 GC 回收。但 value 是强引用可能造成泄漏。所以用完要 removetry{UseruserUserContext.get();}finally{UserContext.remove();// 用完清理}6. 总结每个线程独立副本互不干扰常用场景用户信息传递、数据库连接、链路追踪用完记得 remove防止内存泄漏
ThreadLocal 详解
1. 是什么ThreadLocal 是线程本地变量给每个线程提供独立的变量副本互不影响。2. 简单用法publicclassUserContext{privatestaticfinalThreadLocalStringUSERnewThreadLocal();publicstaticvoidset(Stringuser){USER.set(user);}publicstaticStringget(){returnUSER.get();}publicstaticvoidremove(){USER.remove();}}// 使用UserContext.set(张三);StringuserUserContext.get();// 同一个线程里拿到的都是张三UserContext.remove();3. 常见场景用户信息传递不用每次方法参数传递// 登录时存进去UserContext.set(SecurityContext.getUser());// 任何地方都能取到publicvoidanyMethod(){UseruserUserContext.get();// 拿到当前登录用户}数据库连接保证同一线程用的是同一个连接privatestaticfinalThreadLocalConnectionDBnewThreadLocal();// 获取连接publicConnectiongetConnection(){ConnectionconnDB.get();if(connnull){conndataSource.getConnection();DB.set(conn);}returnconn;}请求TracerprivatestaticfinalThreadLocalStringTRACE_IDnewThreadLocal();publicvoidhandleRequest(){TRACE_ID.set(UUID.randomUUID().toString());log.info(开始处理, traceId{},TRACE_ID.get());// 整个调用链都能拿到同一个 traceId}4. 原理每个 Thread 对象里有个 ThreadLocalMapThread └── threadLocals: ThreadLocalMap ├── ThreadLocal? → value ├── ThreadLocal? → value └── ...set 时把 ThreadLocal 对象作为 key存到当前线程的 Map 里。get 时从当前线程的 Map 里用 ThreadLocal 对象作为 key 取值。所以不同线程访问同一个 ThreadLocal拿到的值不一样。5. 内存泄漏ThreadLocalMap 的 Entry 继承 WeakReferencestaticclassEntryextendsWeakReferenceThreadLocal?{Objectvalue;Entry(ThreadLocal?k,Objectv){super(k);valuev;}}keyThreadLocal 对象是弱引用理论上会被 GC 回收。但 value 是强引用可能造成泄漏。所以用完要 removetry{UseruserUserContext.get();}finally{UserContext.remove();// 用完清理}6. 总结每个线程独立副本互不干扰常用场景用户信息传递、数据库连接、链路追踪用完记得 remove防止内存泄漏