在多线程环境中对于全局变量的使用,往往这个变量存在线程不安全性,通过将对象保存在ThreadLocal中,使得每个线程都拥有自己的对象,达到维持线程封闭性效果。
生产环境中的案例:
- 登录 一般登录实现会选择在登录成功后 将user上下文对象封装在ThreadLocal对象内 ,其他线程需要用到登录的用户ID
说明
一般都会在
HandlerInterceptorAdapter中preHandle方法内 每次从ThreadLocal.get上下文对象 然后重新设置新的内容
public static void setContext(CmsUserTokenVO cmsUserTokenVO) { CmsUserSession.Context context = getContext(); context.setToken(cmsUserTokenVO.getToken()); context.setUserId(cmsUserTokenVO.getUserId()); context.setUserName(cmsUserTokenVO.getUserName()); context.setUserType(cmsUserTokenVO.getUserType()); } public static CmsUserSession.Context getContext() { return LOCAL.get(); }
- dubbo traceId的使用
消费者项目访问线程 在调用完dubbo服务后 拿到traceId并且放到threadLocal变量内 ,于是该线程可以直接获取生产者线程的TraceId方便 分布式traceId日志跟踪 排查问题
/**
*生产者
*/ public class TraceIDProviderFilter implements Filter { public static final String TRACE_ID = "TRACE_ID"; public TraceIDProviderFilter() { } public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException { String traceId = inv.getAttachment("TRACE_ID"); if (StringUtils.isEmpty(traceId)) { traceId = UUID.randomUUID().toString(); } TraceIDUtils.setTraceId(traceId); String mdcData = String.format("[TraceID:%s]", traceId); MDC.put("mdcData", mdcData); Result var5; try { var5 = invoker.invoke(inv); } finally { TraceIDUtils.removeTraceId(); MDC.clear(); } return var5; } }
/**
*消费者
*/
@Activate(
group = {"consumer"}
)
public class TraceIDConsumerFilter implements Filter {
public TraceIDConsumerFilter() {
}
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
if (TraceIDUtils.getTraceId() != null) {
invocation.getAttachments().put("TRACE_ID", TraceIDUtils.getTraceId());
}
return invoker.invoke(invocation);
}
}
示例代码
public class TraceIDUtils { private static final ThreadLocal<String> TRACE_ID = new ThreadLocal<String>(); public static String getTraceId() { return TRACE_ID.get(); } public static void setTraceId(String traceId) { TRACE_ID.set(traceId); } public static void removeTraceId() { TRACE_ID.remove(); } }
注意:
volatile变量上存在一种特殊的线程封闭。只要确保只有单个线程对共享的volatile变量执行写入操作,那么就可以完全的在这些共享volatile变量上执行“读取-修改-写入”操作.
在这种情况下 相当于将修改操作封闭在单个线程中以防止发生竞争条件,并且volatile变量的可见性还能确保其他线程能看到最新的值。