ThreadLocal

在多线程环境中对于全局变量的使用,往往这个变量存在线程不安全性,通过将对象保存在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变量的可见性还能确保其他线程能看到最新的值。

原文地址:https://www.cnblogs.com/zhangfengshi/p/7120149.html