ThreadLocal

博客介绍了ThreadLocal,它可解决多线程的数据安全问题,能为当前线程关联数据,像普通变量、对象、数组、集合等。其特点包括可像Map一样存取数据,每个实例只能关联一个数据,定义时一般为static类型,保存的数据在线程销毁后由JVM自动释放。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ThreadLocal

ThreadLocal 的作用,它可以解决多线程的数据安全问题。ThreadLocal 它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)。

ThreadLocal 的特点:

  1. ThreadLocal 可以为当前线程关联一个数据。(它可以像 Map 一样存取数据,key 为当前线程
  2. 每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal 对象实例。
  3. 每个 ThreadLocal 对象实例定义的时候,一般都是 static 类型
  4. ThreadLocal 中保存数据,在线程销毁后。会由 JVM 虚拟自动释放。

测试类:

public class OrderService {

public void createOrder(){
String name = Thread.currentThread().getName(); System.out.println("OrderService 当前线程[" + name + "]中保存的数据是:" +
ThreadLocalTest.threadLocal.get());
new OrderDao().saveOrder();
}

}

public class OrderDao {

public void saveOrder(){
String name = Thread.currentThread().getName(); System.out.println("OrderDao 当前线程[" + name + "]中保存的数据是:" +
ThreadLocalTest.threadLocal.get());
}

}

public class ThreadLocalTest {

//	public static Map<String,Object> data = new Hashtable<String,Object>();
public static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();

private static Random random = new Random();

public static class Task implements Runnable { @Override
public void run() {
// 在Run 方法中,随机生成一个变量(线程要关联的数据),然后以当前线程名为key 保存到map 中
Integer i = random.nextInt(1000);
// 获取当前线程名
String name = Thread.currentThread().getName(); System.out.println("线程["+name+"]生成的随机数是:" + i);
//		data.put(name,i); threadLocal.set(i);

try { Thread.sleep(3000);
} catch (InterruptedException e) { e.printStackTrace();
}
new OrderService().createOrder();

// 在Run 方法结束之前,以当前线程名获取出数据并打印。查看是否可以取出操作
//	Object o = data.get(name);
Object o = threadLocal.get();
System.out.println("在线程["+name+"]快结束时取出关联的数据是:" + o);
}
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++){
new Thread(new Task()).start();
}
}
### ThreadLocal 的基本概念 `ThreadLocal` 是 Java 中的一个类,用于创建线程本地变量 (thread-local variables)[^1]。这些变量的特点在于每个线程都有自己的独立副本,因此即使多个线程访问同一个 `ThreadLocal` 实例,它们也不会相互干扰。 #### 创建和初始化 ThreadLocal 变量 可以通过以下方式来定义并初始化一个 `ThreadLocal` 对象: ```java ThreadLocal<Integer> threadLocalVariable = new ThreadLocal<>(); // 或者通过提供初始值的方式 ThreadLocal<Integer> initializedThreadLocal = ThreadLocal.withInitial(() -> 0); ``` 上述代码展示了两种不同的方法来实例化 `ThreadLocal` 对象。第一种方式未指定默认值,在首次调用前会返回 `null`;第二种则提供了 lambda 表达式作为其初始值设定逻辑[^2]。 ### 使用场景分析 `ThreadLocal` 常被用来处理多线程环境下的数据隔离问题。例如数据库连接、Session 管理或者事务控制等方面都可以利用它实现每条线程拥有自己单独的状态信息而不影响其他线程的工作状态[^3]。 #### 示例程序展示如何应用 ThreadLocal 来保存当前用户的登录名 ```java public class UserContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); public static void setUserName(String userName){ System.out.println(Thread.currentThread().getName() + ": Setting user name - "+userName); contextHolder.set(userName); } public static String getUserName(){ return contextHolder.get(); } public static void remove(){ contextHolder.remove(); // 清除资源防止内存泄漏 } } class Task implements Runnable{ private String username; public Task(String username){ this.username=username; } @Override public void run() { try { UserContextHolder.setUserName(username); // Simulate some processing time. Thread.sleep(1000L); System.out.println(Thread.currentThread().getName()+": Current UserName is "+UserContextHolder.getUserName()); } catch (InterruptedException e) { e.printStackTrace(); } finally { UserContextHolder.remove(); } } } ``` 在这个例子中,我们模拟了一个简单的任务执行过程,并且使用了 `ThreadLocal` 存储用户名以便于后续操作能够获取到正确的用户身份标识符[^4]。 ### 最佳实践建议 - **及时清理资源**:当不再需要某个特定的 `ThreadLocal` 数据时应该显式地调用 `.remove()` 方法释放关联的对象引用以防潜在的内存泄露风险。 - **避免存储大对象**:由于每个线程都会持有属于它的那份拷贝,如果存放大体积的数据结构可能会造成不必要的开销甚至 OOM 错误。 - **合理设置初值函数**:对于那些可能频繁读取却很少修改的情况可以考虑采用懒加载模式减少性能损耗[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值