多线程 : ThreadLocal 实现线程间共享变量隔离例子

本文介绍如何使用ThreadLocal类将线程范围内共享数据进行封装,通过单例模式和获取实例方法实现数据在不同线程间的共享,并展示了如何在类中直接使用封装好的线程范围内的对象实例调用相应方法。
package thread;

import java.util.Random;

public class ThreadLocalShareDataDemo {

	/**
	 * ThreadLocal类及应用技巧 将线程范围内共享数据进行封装,封装到一个单独的数据类中,提供设置获取方法
	 * 将该类单例化,提供获取实例对象的方法,获取到的实例对象是已经封装好的当前线程范围内的对象
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		for (int i = 0; i < 2; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					int data = new Random().nextInt(889);
					System.out.println(Thread.currentThread().getName() + "产生数据:" + data);
					MyData myData = MyData.getInstance();
					myData.setAge(data);
					myData.setName("Name:" + data);
					new A().get();
					new B().get();
				}
			}).start();
		}

	}

	static class A {
		// 可以直接使用获取到的线程范围内的对象实例调用相应方法
		String name = MyData.getInstance().getName();
		int age = MyData.getInstance().getAge();

		public void get() {
			System.out.println(Thread.currentThread().getName() + "-- AA name:" + name + "...age:" + age);
		}
	}

	static class B {
		// 可以直接使用获取到的线程范围内的对象实例调用相应方法
		String name = MyData.getInstance().getName();
		int age = MyData.getInstance().getAge();

		public void get() {
			System.out.println(Thread.currentThread().getName() + "-- BB name:" + name + "...age:" + age);
		}
	}

	static class MyData {

		// 将实例对象存入当前线程范围内数据集中
		static ThreadLocal<MyData> threadLocal = new ThreadLocal<MyData>();

		// 单例
		private MyData() {
		};

		// 提供获取实例方法
		public static MyData getInstance() {
			// 从当前线程范围内数据集中获取实例对象
			MyData instance = threadLocal.get();
			if (instance == null) {
				instance = new MyData();
				threadLocal.set(instance);
			}
			return instance;
		}

		private String name;
		private int age;

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public int getAge() {
			return age;
		}

		public void setAge(int age) {
			this.age = age;
		}
	}

}

### Java ThreadLocal 实现线程安全的原理及应用场景 #### 1. **ThreadLocal 的基本概念** `ThreadLocal` 是 Java 提供的一个类,允许为每个线程维护一份独立的变量副本。不同线程之间无法访问彼此的 `ThreadLocal` 变量,从而实现线程间隔离[^1]。 --- #### 2. **ThreadLocal实现原理** `ThreadLocal` 的核心在于它的内部存储结构和工作机制: - 每个线程都有一个与其绑定的 `ThreadLocalMap` 对象。 - 这个 `ThreadLocalMap` 存储的是键值对,其中键是 `ThreadLocal` 的实例,值则是对应线程的局部变量。 - 当调用 `ThreadLocal.set(value)` 方法时,实际是在当前线程的 `ThreadLocalMap` 中存入了一个以当前 `ThreadLocal` 实例为键、指定值为值的映射关系。 - 调用 `ThreadLocal.get()` 方法时,则是从当前线程的 `ThreadLocalMap` 中取出与当前 `ThreadLocal` 实例关联的值。 这种设计使得即使多个线程共享同一个 `ThreadLocal` 实例,由于每个线程都拥有自己独立的 `ThreadLocalMap`,因此不会发生数据冲突[^3]。 --- #### 3. **ThreadLocal 的内存泄漏风险** 虽然 `ThreadLocal` 提供了线程安全性,但如果管理不当可能会引发内存泄漏问题: - `ThreadLocalMap` 中使用的键是弱引用(Weak Reference),而值却是强引用。 - 如果某个 `ThreadLocal` 实例被外部释放掉,但由于其值仍然驻留在 `ThreadLocalMap` 中未能及时清理,就可能导致垃圾回收器无法回收这些对象,进而造成内存泄漏。 - 解决方案:在不再需要 `ThreadLocal` 变量时显式调用 `remove()` 方法将其从 `ThreadLocalMap` 中移除[^1]。 --- #### 4. **ThreadLocal 的典型应用场景** 以下是几个常见的使用场景: - **数据库连接池**:在线程中保存数据库连接对象,避免频繁创建销毁连接带来的性能开销。 - **事务管理**:如 Spring 中利用 `TransactionSynchronizationManager` 维护事务上下文信息,确保同一事务内的操作能够正确传播。 - **日志记录**:为每个请求分配唯一的 ID,并贯穿整个业务流程以便追踪调试。 - **用户身份认证信息传递**:在一个复杂的分布式系统里,通过 `ThreadLocal` 在各层间透明地携带登录用户的凭证等敏感资料[^3]。 --- #### 示例代码:简单演示 ThreadLocal 的使用 下面是一段展示如何使用 `ThreadLocal` 的例子: ```java public class ThreadLocalExample { private static final ThreadLocal<Integer> threadLocalValue = new ThreadLocal<>(); public static void main(String[] args) { Runnable task = () -> { int value = (int)(Math.random() * 100); // 随机生成一个整数作为本线程的私有值 threadLocalValue.set(value); System.out.println(Thread.currentThread().getName() + ": " + threadLocalValue.get()); threadLocalValue.remove(); // 清理资源防止潜在的内存泄露 }; Thread t1 = new Thread(task, "Thread-1"); Thread t2 = new Thread(task, "Thread-2"); t1.start(); t2.start(); } } ``` 在这段代码中,两个不同的线程各自设置了属于自己的随机数值并通过 `ThreadLocal` 获取到互不影响的结果。 --- ### 常见面试题及相关解析 1. **什么是 ThreadLocal?它是怎么工作的?** - 答案已详述于前文中提到的工作机制部分。 2. **为什么说 ThreadLocal 不是用来解决多线程并发问题的?** - 因为其本质并不是同步控制手段,而是提供了一种方式让每个线程都有自己单独的一份数据拷贝,从根本上避开了竞争条件的发生可能性[^3]。 3. **ThreadLocal 是否会造成内存泄漏?如果有该如何预防?** - 已经讨论过这个问题并给出了建议措施即适时清除无用条目以减少隐患[^1]。 4. **Spring 框架是如何运用 ThreadLocal 技术提升效率的同时保障线程安全性的呢?** - 春季框架借助诸如 RequestContextHolder 等工具类把原本非线程安全的一些组件改造成具备线程专属性质之后再交付给单例模式下的服务单元重复利用。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值