对象传递
在一个线程中,如果我们想要传递一个对象,一般是怎么做的呢?我们看下面这个方法,通过传参的方式可以在方法之间传递对象:
public void process(Order order) {
checkParams(order);
createOrder(order);
notifyMessages(order);
}
这样做一般来说是没有任何问题的,但是方法中的方法有时候又会调用其他很多方法,比如:
public void createOrder(Order order) {
checkIfExists(order);
doCreate(order);
}
这样会导致Order对象传递到所有地方。
ThreadLocal
这种在一个线程中,在若干方法中调用,需要传递的对象,我们通常称之为上下文(Context),它是一种状态。给每个方法增加一个 context 参数非常麻烦,并且有时候,如果调用链有无法修改源码的第三方库,我们的对象就传不进去了。
Java 标准库提供了一个特殊的ThreadLocal,它可以在一个线程中传递同一个对象。
我们通常使用如下方式来初始化一个ThreadLocal对象:
public static ThreadLocal threadLocal = new ThreadLocal<>();
他的使用方法也非常简单:
void processUser(Order order) {
try {
threadLocal.set(order);
doSomething();
doOthers();
} finally {
threadLocal.remove();
}
}
通过设置一个Order实例关联到ThreadLocal中,在移除之前,所有方法都可以随时获取到该Order实例。
void doSomething() {
Order order = threadLocalUser.get();
System.out.println(order);
}
void doOthers() {
Order order = threadLocalUser.get();
System.out.println(order);
}
实际上,可以把ThreadLocal看成一个全局MapThreadLocal变量时,总是使用Thread自身作为key:
Object threadLocalValue = threadLocalMap.get(Thread.currentThread());
因此,ThreadLocal相当于给每个线程都开辟了一个独立的存储空间,各个线程的ThreadLocal关联的实例互不干扰。
最后,特别注意ThreadLocal一定要在finally中清除。这是因为当前线程执行完相关代码后,很可能会被重新放入线程池中,如果ThreadLocal没有被清除,该线程执行其他代码时,会把上一次的状态带进去。
为了保证能释放ThreadLocal关联的实例,我们可以通过AutoCloseable接口配合try (resource) {...}结构,让编译器自动为我们关闭。例如,一个保存了当前订单id的ThreadLocal可以封装为一个OrderContext对象:
public class OrderContext implements AutoCloseable {
static final ThreadLocal ctx = new ThreadLocal<>();
public OrderContext(String orderId) {
ctx.set(orderId);
}
public static String currentOrderId() {
return ctx.get();
}
@Override
public void close() {
ctx.remove();
}
}
使用的时候,我们借助try (resource) {...}结构,可以这么写:
try (var ctx = new OrderContext("Bob")) {
String currentOrderId = OrderContext.currentOrderId();
}
这样就在OrderContext中完全封装了ThreadLocal,外部代码在try (resource) {...}内部可以随时调用OrderContext.currentOrderId()获取当前线程绑定的订单id。
写在最后
•
ThreadLocal表示线程的“局部变量”,它确保每个线程的ThreadLocal变量都是各自独立的•
ThreadLocal适合在一个线程的处理流程中保持上下文(避免了同一参数在所有方法中传递)• 使用
ThreadLocal要用try ... finally结构,并在finally中清除