从上述结果可以看出,使用 ThreadLocal 也可以解决线程并发问题,并且避免了代码加锁排队执行的问题。
使用场景2:跨类传递数据除了上面的使用场景之外,我们还可以使用 ThreadLocal 来实现线程中跨类、跨方法的数据传递。比如登录用户的 User 对象信息,我们需要在不同的子系统中多次使用,如果使用传统的方式,我们需要使用方法传参和返回值的方式来传递 User 对象,然而这样就无形中造成了类和类之间,甚至是系统和系统之间的相互耦合了,所以此时我们可以使用 ThreadLocal 来实现 User 对象的传递。
确定了方案之后,接下来我们来实现具体的业务代码。我们可以先在主线程中构造并初始化一个 User 对象,并将此 User 对象存储在 ThreadLocal 中,存储完成之后,我们就可以在同一个线程的其他类中,如仓储类或订单类中直接获取并使用 User 对象了,具体实现代码如下。
主线程中的业务代码:
public class ThreadLocalByUser {
public static void main(String[] args) {
// 初始化用户信息
User user = new User("Java");
// 将 User 对象存储在 ThreadLocal 中
UserStorage.setUser(user);
// 调用订单系统
OrderSystem orderSystem = new OrderSystem();
// 添加订单(方法内获取用户信息)
orderSystem.add();
// 调用仓储系统
RepertorySystem repertory = new RepertorySystem();
// 减库存(方法内获取用户信息)
repertory.decrement();
}
}
User 实体类:
/**
* 用户实体类
*/
class User {
public User(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
ThreadLocal 操作类:
/**
* 用户信息存储类
*/
class UserStorage {
// 用户信息
public static ThreadLocal<User> USER = new ThreadLocal();
/**
* 存储用户信息
* @param user 用户数据
*/
public static void setUser(User user) {
USER.set(user);
}
}
订单类:
/**
* 订单类
*/
class OrderSystem {
/**
* 订单添加方法
*/
public void add() {
// 得到用户信息
User user = UserStorage.USER.get();
// 业务处理代码(忽略)...
System.out.println(String.format("订单系统收到用户:%s 的请求。",
user.getName()));
}
}
仓储类:
/**
* 仓储类
*/
class RepertorySystem {
/**
* 减库存方法
*/
public void decrement() {
// 得到用户信息
User user = UserStorage.USER.get();
// 业务处理代码(忽略)...
System.out.println(String.format("仓储系统收到用户:%s 的请求。",
user.getName()));
}
}
以上程序的最终执行结果:
从上述结果可以看出,当我们在主线程中先初始化了 User 对象之后,订单类和仓储类无需进行任何的参数传递也可以正常获得 User 对象了,从而实现了一个线程中,跨类和跨方法的数据传递。
总结使用 ThreadLocal 可以创建线程私有变量,所以不会导致线程安全问题,同时使用 ThreadLocal 还可以避免因为引入锁而造成线程排队执行所带来的性能消耗;再者使用 ThreadLocal 还可以实现一个线程内跨类、跨方法的数据传递。
参考 & 鸣谢《码出高效:Java开发手册》
《Java 并发编程 78 讲》
,