引用
前面我有两篇文章介绍了
ThreadLocal
,明确了ThreadLocal
是干什么的,并且弄清楚了他的内存泄漏风险和挑战,这里我将详细介绍一下ThreadLocal
的一些应用和封装方法。
一、学会合理设置初始值
通过重写 initialValue()
方法,可以为 ThreadLocal
变量设置一个合理的初始值,这样可以避免在没有显式设置值的情况下 get()
方法返回 null
。
首先来说,正常我们要用到ThreadLocal
的时候,实际一般情况我们都会封装一个工具类,那么在这个工具类里面,我们怎么设置初始值呢?跟我一起瞧瞧:
下面的案例用于切换数据源:
public class DynamicDataSourceHolder {
private static final ThreadLocal<String> contextHolder = ThreadLocal.withInitial(() -> DataSourceConstant.DEFAULT_DS);
/**
* 设置数据源类型
*
* @param dataSourceType
*/
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
/**
* 获取数据源类型
*/
public static String getDataSourceType() {
// 假如上面没有设置默认值,这里就需要增加空判断
// if (contextHolder.get() == null) {
// return DataSourceConstant.DEFAULT_DS;
// }
return contextHolder.get();
}
/**
* 清除数据源类型
*/
public static void clearDataSourceType() {
contextHolder.remove();
}
}
注意:假如我们不设置默认值,那么在18,19,20,21行就需要增加空判断
二、使用 InheritableThreadLocal
当需要在创建子线程时传递父线程的 ThreadLocal
变量时,可以使用 InheritableThreadLocal
。这样,子线程会自动继承父线程中的 ThreadLocal
变量值。
这玩意的用法和ThreadLocal
大差不差,但是他最大的特点就是,儿子完美继承父亲的基因,但是又有自己的价值观,也可以理解为我们玩单机游戏,老是喜欢给自己创建副本,当然也要注意使用姿势,别炸内存了。
使用场景:在很多时候我们会启动一个异步线程,但是有希望前面设定的特定信息能够在后续线程中也可以使用,这个时候我们就可以考虑用一下InheritableThreadLocal
。
三、封装 ThreadLocal 变量
可以将 ThreadLocal
变量封装在一个实现了 AutoCloseable
接口的类中,然后在 try-with-resources
语句中使用它。这样可以确保在结束时自动清理 ThreadLocal
变量,避免内存泄漏。
3.1 实现步骤
-
定义一个类:创建一个类,这个类内部持有一个
ThreadLocal
变量。 -
实现
AutoCloseable
接口:在这个类中实现AutoCloseable
接口,这样你就可以在try-with-resources
语句中使用它。 -
在
close
方法中清理:在AutoCloseable
的close
方法中添加清理ThreadLocal
的逻辑。 -
使用
try-with-resources
语句:在需要使用ThreadLocal
变量的地方,使用try-with-resources
语句,这样可以确保close
方法在资源使用完毕后被调用。
3.2 使用案例
- 实现封装代码
import java.io.IOException;
public class ThreadLocalResource<T> implements AutoCloseable {
private final ThreadLocal<T> threadLocal;
public ThreadLocalResource(T initialValue) {
this.threadLocal = new ThreadLocal<>();
this.threadLocal.set(initialValue);
}
public T get() {
return threadLocal.get();
}
public void set(T value) {
threadLocal.set(value);
}
@Override
public void close() throws IOException {
threadLocal.remove();
System.out.println("###### 自动清理完毕");
}
}
- 使用方法
import java.io.IOException;
public class Main {
public static void main(String[] args) {
/* 使用 try-with-resources 语句,确保 ThreadLocal 变量被自动清理 */
try (ThreadLocalResource<String> resource = new ThreadLocalResource<>("我是新来的")) {
// 在这里使用 resource
System.out.println("初始值: " + resource.get());
resource.set("我是后面来的");
System.out.println("设置后的值: " + resource.get());
} catch (IOException e) {
e.printStackTrace();
}
// 执行到这里已经自动清理了
System.out.println("程序执行完毕");
}
}
- 执行结果
注意:从下面的执行结果可以看到当代码执行到16行的时候,已经完成了自动清理
初始值: 我是新来的
设置后的值: 我是后面来的
###### 自动清理完毕
程序执行完毕
四、总结
在上面介绍了三种使用ThreadLocal
的方法,实际上,这三种使用方法是可以结合使用的,甚至可以三者一起结合,具体使用情况要看业务场景需要整理。
到这里关于ThreadLocal
的介绍已经完结了,感兴趣的也可以去看看前面两篇。
希望本文对您有所帮助。如果有任何错误或建议,请随时指正和提出。
同时,如果您觉得这篇文章有价值,请考虑点赞和收藏。这将激励我进一步改进和创作更多有用的内容。
感谢您的支持和理解!