首页 > 其他分享 >如何避免忘记清理 ThreadLocal ?

如何避免忘记清理 ThreadLocal ?

时间:2023-03-20 11:32:44浏览次数:42  
标签:get 清理 ThreadLocal static CONTEXT 忘记 上下文 public con


一、背景

ThreadLocal 可以解决“线程安全问题”。

如何避免忘记清理 ThreadLocal ?_面试

也可以作为上下文暂存数据以备后续步骤获取。

但是 ThreadLocal 用不好的确容易产生故障,因而有些团队不允许使用 ThreadLocal。

最核心的一个原因是很容易忘记清理,在线程池环境下复用导致串环境。

如何避免忘记清理 ThreadLocal ?_面试_02

那么,有什么优雅的解法没?本文给出自己的一个解法。

二、解法

package basic.thread;

import com.alibaba.ttl.TransmittableThreadLocal;

import java.util.HashMap;
import java.util.Map;

public class ThreadContext {

private static final ThreadLocal<Map<String, Object>> CONTEXT = new TransmittableThreadLocal<>();

/**
* 初始化上下文
*/
public static void initContext() {
Map<String, Object> con = CONTEXT.get();
if (con == null) {

CONTEXT.set(new HashMap<>(8));
} else {
CONTEXT.get().clear();
}
}

/**
* 清除上下文
*/
public static void clearContext() {
CONTEXT.remove();
}

/**
* 获取上下文内容
*/
public static <T> T getValue(String key) {
Map<String, Object> con = CONTEXT.get();
if (con == null) {
return null;
}
return (T) con.get(key);
}

/**
* 设置上下文参数
*/
public static void putValue(String key, Object value) {
Map<String, Object> con = CONTEXT.get();
if (con == null) {
CONTEXT.set(new HashMap<>(8));
con = CONTEXT.get();
}
con.put(key, value);
}
}

2.1 Java 开发手册中的建议

如何避免忘记清理 ThreadLocal ?_封装_03

写入如下:

public Result<R> executeAbility(T ability) {
//初始化上下文
ThreadContext.initContext();
try {
//省略核心业务代码

} finally {
ThreadContext.clearContext();
}
}

2.2 进一步改进

相信绝大多数人会止步于此,但我认为这还是不够的。
如何才能避免忘掉清理 threadlocal 呢?

JDK 源码中有没有类似的案例呢?
想想IO 读写文件后,也是需要采用类似的做法去释放资源,JDK 提供了 try-with-resource 让释放资源更简单,使用者不需要手动写 finnaly 去释放资源。

普通案例:

如何避免忘记清理 ThreadLocal ?_面试_04


使用 try-with-resource

如何避免忘记清理 ThreadLocal ?_java_05

另外我们知道,可以通过实现 AutoCloseable 来自定义 try-with-resource 的资源。

但最后发现并不是很适配,因为在传递上下文这种场景下, ThreadLocal 工具类通常都是静态的,而且即使不适用静态,获取属性时还要将该对象传递下去,不是很方便。

当然,如果大家不想以静态的方式使用,也可以考虑实现 AutoClosebale 接口,使用 try-with-resource 的机制。

我们是否也可以采用类似的机制呢?

可以直接将初始化和清理方法私有化,提供无参和带返回值的封装,使用 Runnbale 和 Callable 将调用作为参数传入,在封装的方法中封装 try- finally 逻辑。

package basic.thread;

import com.alibaba.ttl.TransmittableThreadLocal;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;

public class ThreadContext {

private static final ThreadLocal<Map<String, Object>> CONTEXT = new TransmittableThreadLocal<>();

/**
* 初始化上下文
*/
private static void initContext() {
Map<String, Object> con = CONTEXT.get();
if (con == null) {

CONTEXT.set(new HashMap<>(8));
} else {
CONTEXT.get().clear();
}
}

/**
* 清除上下文
*/
private static void clearContext() {
CONTEXT.remove();
}

/**
* 获取上下文内容
*/
public static <T> T getValue(String key) {
Map<String, Object> con = CONTEXT.get();
if (con == null) {
return null;
}
return (T) con.get(key);
}

/**
* 设置上下文参数
*/
public static void putValue(String key, Object value) {
Map<String, Object> con = CONTEXT.get();
if (con == null) {
CONTEXT.set(new HashMap<>(8));
con = CONTEXT.get();
}
con.put(key, value);
}

/**
* 自动回收的封装
*/
public static void runWithAutoClear(Runnable runnable){
initContext();
try{
runnable.run();
}finally{
CONTEXT.remove();
}
}

/**
* 自动回收的封装
*/
public static <T> T callWithAutoClear(Callable<T> callable){
initContext();
try{
try {
return callable.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
}finally{
CONTEXT.remove();
}
}
}

使用参考:

public Result<R> executeAbility(T ability) {
return ThreadContext.callWithAutoClear(()->{
// 业务核心代码
});
}

2.3 切面

定义支持上下文注解,如 ​​@EnableThreadContext​​ 然后对该注解进行切面,执行前初始化上下文,执行后清理上下文即可。

具体代码省略。

三、一些疑问

3.1 通常线程上下文会跨类使用,所以这么做是不是没意义?

通常线程上下文工具类套在需要使用该上下文工具的最外层即可。也可以直接套在 RPC 的接口实现层或者 Controller 的方法上。

整个调用如果涉及多个类,只要在同一个线程中或者由同一个线程发起(使用 ​​TransmittableThreadLocal​​),子函数或者线程调用的方法中依然可以使用 ThreadContext 的 put 或者 get 方法。

四、总结

只要思想不滑坡,办法总比困难多。

我们应该想办法去解决问题,而不是你回避问题。
当看到有些解决方案仍然容易出错时,应该想办法去做进一步的改进。

当然,如果不想使用 ThreadLocal 还想暂存对象给后续环节使用,可以定义上下文对象,在不同的执行步骤间传递。

类似的文章还有:​​《Map 有变动时触发特定行为实现》​​


创作不易,如果本文对你有帮助,欢迎点赞、收藏加关注,你的支持和鼓励,是我创作的最大动力。

如何避免忘记清理 ThreadLocal ?_封装_06


标签:get,清理,ThreadLocal,static,CONTEXT,忘记,上下文,public,con
From: https://blog.51cto.com/mingmingruyue/6132493

相关文章