ThreadLocal
一、概述
在正常的 Web 开发过程中,对于每个接口,解析 Token,读取用户ID,每个方法都要传入 userId, 是不是也写过这种“流水线”代码?
private void handle(String token) {
// 1. 从 token 中获取 userId
Long userID = getUseIdFromToken(token);
// 2. 验证用户的权限
validateUserPermission(userID);
// 3. 发起审批操作
doExecute(userID);
// 4. 记录操作日志
saveOperationLog(userID);
}
private Long getUseIdFromToken(String token) {
return 0L;
}
private void validateUserPermission(Long userId) {}
private void sendAppeal(Long userId) {}
private void saveOperationLog(Long userId) {}
当然了,这里的 token 实际上是从请求头或者其余方式读取到的。而这样的写法就会导致:
1)每一个接口,都需要写一遍获取并解析 Token 的业务逻辑
2)如果业务方法有需要用到 userId ,就需要在方法声明之中定义,一层一层往下传
为了解决这些问题,就需要用到 ThreadLocal,它是一个线程内部独享的变量,每个线程访问 ThreadLocal 的时候,访问的都是自己线程的私有变量。这样,我们的流程就可以改为:
1)解析 token,读取用户ID,将其放入都 ThreadLocal 中
2)业务方法从 ThreadLocal 直接取
接下来,我们首先来看一下具体的使用方式:
public static final ThreadLocal<Long> USERID_HOLDER = new ThreadLocal<>();
private void handle(String token) {
// 1. 从 token 中获取 userId
getUseIdFromToken(token);
// 2. 验证用户的权限
validateUserPermission();
// 3. 发起审批操作
sendAppeal();
// 4. 记录操作日志
saveOperationLog();
// 5. 清理 ThreadLocal
USERID_HOLDER.remove();
}
private void getUseIdFromToken(String token) {
USERID_HOLDER.set(1L);
}
private void validateUserPermission() {
Long userId = USERID_HOLDER.get();
}
private void sendAppeal() {
Long userId = USERID_HOLDER.get();
}
private void saveOperationLog() {
Long userId = USERID_HOLDER.get();
}
一套操作下来,代码量腰斩,可读性 + 可维护性直线上升。在上面的代码之中,我们有两点是需要考虑的:
1)ThreadLocal 为什么要定义为:satic final
2)当执行完成操作之后,为什么需要清理 ThreadLocal 的值?不清理会造成什么后果

浙公网安备 33010602011771号