ThreadLocal介绍
ThreadLocal
内部维护的是一个ThreadLocalMap
的数据结构(类似于Map);- 每个线程都可以通过
set()
和get()
来对这个局部变量进行操作,当然还有remove()
方法; - 同一个
ThreadLocal
所包含的对象,在不同的Thread
中有不同的副本; - 适合那些需要公共方法,但私有变量的场景,如获取当前用户;
ThreadLocalMap结构
- ThreadLocalMap里面维护了一个Entry,
key
为使用弱引用的ThreadLocal
实例,value
为线程变量的副本;
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocal使用场景
- 保存线程不安全的类,例如:
SimpleDateFormat
; - 在web开发中,可以保存用户的登陆信息;
总结:适用于变量在线程间隔离且在方法间共享的场景;
什么是内存泄露
- 不再被使用的对象或者变量占用的内存不能被回收,就是内存泄露;
在这里要引到另外一个知识点,就是弱引用,不懂的看这篇文章;
通过以上文章会得知:当系统进行GC时,只要垃圾回收器扫到,不管空间够不够,都会被回收;
下图中的虚线就是弱引用,如果系统进行了一次GC,那么当前线程还在,但是key会被回收;
也就是key没了,但value还在,因为value被线程 ThreadLocalMap
的 Entry
强引用;
不过value没有了key,也就永远都不会被操作,永远的留在了内存中,直到系统OOM;
如何防止内存泄漏
- 每次使用完
ThreadLocal
都调用它的remove()
方法清除数据; - 将
ThreadLocal
变量定义成private static
,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal
的弱引用访问到Entry的value值,进而清除掉;
使用案例
/**
* 数据源切换处理
*
*/
@Slf4j
public class DynamicDataSourceContextHolder {
/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源的变量
*/
public static void setDataSourceType(String dsType) {
log.info("切换到{}数据源", dsType);
CONTEXT_HOLDER.set(dsType);
}
/**
* 获得数据源的变量
*/
public static String getDataSourceType() {
return CONTEXT_HOLDER.get();
}
/**
* 清空数据源变量
*/
public static void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
}
参考:https://juejin.cn/post/6844903910679773192