本意想做一个线程内共享、线程之间隔离的工具类,首先考虑的是ThreadLocal的封装,让gpt帮我写了一个。
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ThreadLocalContext {
private static final ThreadLocal<Map<String, Object>> THREAD_LOCAL = ThreadLocal.withInitial(ConcurrentHashMap::new);
public static void put(String key, Object value) {
THREAD_LOCAL.get().put(key, value);
}
public static Object get(String key) {
return THREAD_LOCAL.get().get(key);
}
public static void remove(String key) {
THREAD_LOCAL.get().remove(key);
}
public static Map<String, Object> getAll() {
return new ConcurrentHashMap<>(THREAD_LOCAL.get());
}
public static void clear() {
THREAD_LOCAL.get().clear();
// remove会反复调用ThreadLocal的 setInitialValue 方法
// THREAD_LOCAL.remove();
}
}
其实slf4j提供了一个类似的工具类MDC,但是这个MDC里的值可以被日志配置文件共享,比如输出的控制台内容里可以加工号,把登陆人工号写到MDC中,业务日志里就不需要再拼userNo了。
SLF4J(Simple Logging Facade for Java)是一个轻量级的日志框架,它使用日志记录代替OGNL(Object Graph Notation)语句来描述日志输出。SLF4J提供了一个统一的API,可以方便地将不同的日志框架和工具集成到应用程序中。 SLF4J提供了一个MDC(Mapped Diagnostic Context)上下文类,它允许应用程序在运行时动态地将上下文对象附加到日志输出上。当一个日志输出器输出日志时,SLF4J会自动将MDC对象附加到日志输出中,这样就可以方便地记录应用程序的运行时环境信息和错误信息。 SLF4J的MDC上下文类包含了一些方法,例如get、put、remove和clear,可以用来动态地修改MDC对象。使用SLF4J的MDC上下文类可以使日志输出更加灵活和可维护,并且可以方便地记录应用程序的运行时环境信息和错误信息。
用threadLocal主要考虑的就是内存泄漏的问题。因为ThreadLocal是线程的引用,只要线程存在对象就一直存在,但是里面的key-value可能过期了不在使用了,而且在web中线程池里的线程是不断复用的,每次复用时上一次线程使用的值会引起这次逻辑的错误,比如上次缓存了工号1,第二次复用没清除也没加载新的工号,变成了2。所以要确保每次使用完后被清除了。
因此threadLocal的clear()调用需要放在足够靠前的过滤器上(顺序靠前,对应的请求执行完的栈退出就靠后,执行顺序靠后能保证clear()不影响业务,比如业务还没用完,你就清除了所有内容,会影响其他业务),并且要通过finally保证clear一定被执行,并且创建端本身要注意remove(key)。