当前位置: 首页 > news >正文

ThreadLocal用法及实现原理解析

ThreadLocal 基本用法

1. 创建和基本使用

public class ThreadLocalDemo {// 创建ThreadLocal变量private static ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 在主线程中设置值threadLocal.set("主线程的值");// 创建新线程Thread thread = new Thread(() -> {// 在新线程中设置值threadLocal.set("子线程的值");System.out.println("子线程: " + threadLocal.get());});thread.start();try {thread.join();} catch (InterruptedException e) {e.printStackTrace();}// 主线程获取自己的值System.out.println("主线程: " + threadLocal.get());}
}

输出结果:

子线程: 子线程的值
主线程: 主线程的值

2. 使用初始值

public class ThreadLocalWithInitialValue {// 使用withInitial提供初始值private static ThreadLocal<Integer> counter = ThreadLocal.withInitial(() -> 0);public static void main(String[] args) {// 每个线程第一次访问时都会使用初始值System.out.println("初始值: " + counter.get());counter.set(counter.get() + 1);System.out.println("增加值后: " + counter.get());}
}

3. 实际应用场景 - 用户上下文管理

public class UserContext {private static ThreadLocal<User> userContext = new ThreadLocal<>();public static void setCurrentUser(User user) {userContext.set(user);}public static User getCurrentUser() {return userContext.get();}public static void clear() {userContext.remove();}// 用户类static class User {private String name;private String id;public User(String name, String id) {this.name = name;this.id = id;}// getters and setters...}
}// 使用示例
public class UserService {public void processUserRequest() {// 在过滤器或拦截器中设置用户信息UserContext.setCurrentUser(new User("张三", "123"));try {// 业务处理,任何地方都可以获取用户信息doBusinessLogic();} finally {// 清理,防止内存泄漏UserContext.clear();}}private void doBusinessLogic() {User user = UserContext.getCurrentUser();System.out.println("处理用户: " + user.getName());}
}

ThreadLocal 实现原理

核心数据结构

// Thread类中有个ThreadLocalMap类型的变量
public class Thread implements Runnable {ThreadLocal.ThreadLocalMap threadLocals = null;
}// ThreadLocalMap是ThreadLocal的内部静态类
static class ThreadLocalMap {// Entry继承自WeakReference,key是弱引用static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);  // 弱引用value = v;}}private Entry[] table;private int size = 0;
}

源码分析

1. set方法
public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {map.set(this, value);} else {createMap(t, value);}
}ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}
2. get方法
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();
}private T setInitialValue() {T value = initialValue();  // 可重写的方法Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {map.set(this, value);} else {createMap(t, value);}return value;
}
3. ThreadLocalMap的set方法
private void set(ThreadLocal<?> key, Object value) {Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);// 线性探测解决哈希冲突for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();if (k == key) {e.value = value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}tab[i] = new Entry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();
}

内存泄漏问题

问题根源

// Entry的key是弱引用,value是强引用
static class Entry extends WeakReference<ThreadLocal<?>> {Object value;  // 强引用!Entry(ThreadLocal<?> k, Object v) {super(k);  // 弱引用value = v;}
}

内存泄漏场景

public class MemoryLeakDemo {public static void main(String[] args) {ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();// 设置大对象threadLocal.set(new byte[1024 * 1024 * 10]); // 10MB// 线程结束前没有remove,可能导致内存泄漏// threadLocal.remove(); // 应该调用这个}
}

正确用法

public class CorrectUsage {private static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));public void process() {try {SimpleDateFormat format = dateFormatThreadLocal.get();// 使用format...} finally {// 重要:使用完毕后清理dateFormatThreadLocal.remove();}}
}

使用建议和最佳实践

1. 使用try-finally确保清理

public class SafeThreadLocalUsage {private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<>();public void doInTransaction() {try {Connection conn = getConnection();connectionHolder.set(conn);// 业务逻辑...} finally {Connection conn = connectionHolder.get();if (conn != null) {connectionHolder.remove();// 其他清理工作...}}}
}

2. 使用InheritableThreadLocal传递线程上下文

public class InheritableThreadLocalDemo {private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {inheritableThreadLocal.set("父线程的值");Thread childThread = new Thread(() -> {// 子线程可以继承父线程的值System.out.println("子线程获取: " + inheritableThreadLocal.get());});childThread.start();}
}

总结

ThreadLocal的核心特点:

  • 每个线程有自己独立的变量副本

  • 通过Thread内部的ThreadLocalMap实现

  • key是ThreadLocal对象的弱引用,value是强引用

  • 需要手动管理内存,避免内存泄漏

适用场景:

  • 线程上下文传递(用户信息、事务管理等)

  • 线程安全的对象复用(如SimpleDateFormat)

  • 避免方法参数传递

注意事项:

  • 使用后务必调用remove()清理

  • 避免存储大对象

  • 考虑使用try-finally确保清理

http://www.dtcms.com/a/566501.html

相关文章:

  • 太原建筑市场网站中国建设银行邢台分行网站
  • 怎样利用关键词来打动读者
  • Deinterleaving of Mixtures of Renewal Processes
  • 设计师网站有哪些wordpress安装多说
  • 哪有网站给光头强做面企业官网小程序源码
  • 大同建设银行保安招聘网站淄博网站制作价格低
  • C#上位机卡顿解决方法1——获取内存占用率
  • 各大搜索引擎网站登录入口网站帮企业做推广价格怎么算
  • 哈尔滨网站建设优化网站页面构成要素
  • OCR图片识别翻译工具功能及源码
  • vue3 抽取el-drawer子组件
  • 杭州专业网站建设佛山cms模板建站
  • TCP可靠传输的秘密:从滑动窗口到拥塞控制
  • 宝塔做网站安全吗做网站龙华
  • safetensors转为gguf,并在ollama中部署
  • 做二手车按揭的网站艺术培训机构
  • 如何给网站做右侧导航互联网网络推广公司
  • 公司网站优化推广宁波企业名称查询网站
  • 做淘宝客网站服务器高新网站建设
  • Mysql 读书笔记
  • 网上做任务佣金高的网站wordpress付费浏览
  • Flutter---卡片交换器
  • MAC-SQL 算法一
  • 大连爱得科技网站建设公司怎么样在线设计平台都有哪些比较好用的
  • 【2051】【例3.1】偶数
  • 北京网站开发外包做网站看什么书
  • 怎么做网站推广临沂世界网站
  • C# 使用XML文件保存配方数据
  • 小说网站自主建设网站域名申请
  • 西安谁家的集团门户网站建设比较好上海公司车牌