在没看过ThreadLocal源码以前,以为是在ThreadLocal中维护了一个Map,然后将当前线程作为key,获取线程对应的变量。所以很多时候真的不能想当然,实践才是检验真理的唯一标准,就让我们一起看看它的实现原理:
浅谈实现原理
先看看Thread类,可以看到在Thread类中维护了一个ThreadLocal.ThreadLocalMap,证明我开始的猜想确实是错的。
ThreadLocal.set(…)
/**
*将此线程局部变量的当前线程副本设置为指定值。
*大多数子类将不需要重写此方法,而仅依靠initialValue方法来设置线程局部变量的值。
*/
public void set(T value) {
// 获取到当前线程
Thread t = Thread.currentThread();
// 获取到存放线程变量的Map(但是这个Map是ThreadLocal中的静态内部类)
ThreadLocalMap map = getMap(t);
if (map != null)
// key为当前ThreadLocal对象本身
map.set(this, value);
else
// 如果map为空就创建,将value作为第一个值存入
createMap(t, value);
}
// 这里可以看到是获取当前线程对象上的一个属性
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocal.get()
/**
* 返回此线程局部变量的当前线程副本中的值。
* 如果变量没有当前线程的值,则首先将其初始化为通过调用initialValue方法返回的值
* @return
*/
public T get() {
Thread t = Thread.currentThread();
// 获取当前线程上的ThreadLocal.ThreadLocalMap对象
ThreadLocal.ThreadLocalMap map = getMap(t);
// 如果map不为空则将当前ThreadLocal对象作为key从map中获取值
if (map != null) {
ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private T setInitialValue() {
// 这里初始值T为null
T value = initialValue();
Thread t = Thread.currentThread();
// 从当前线程上获取map
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
// 如果当前线程不存在ThreadLocal.ThreadLocalMap对象就创建并设置firstValue为null
createMap(t, value);
return value;
}
也就是说是每个Thread对象下维护了一个ThreadLocal.ThreadLocalMap对象,通过调用ThreadLocal.get或ThreadLocal.set方法的时候是将ThreadLocal对象自身作为key从Thread对象维护的ThreadLocalMap对象中存值取值。
ThreadLocal在spring中的应用
相信很多人都用过RequestContextHolder类
public abstract class RequestContextHolder {
private static final boolean jsfPresent =
ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");
.......
.......
}
/**
* 使用
*/
public void getRequest(){
......
// web环境中获取当前请求对象
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
......
}
实际开发中的应用
比如线程不安全的类的使用,比如不同请求存放用户的信息等等。
例1直接贴一串代码:
我们知道SimpleDateFormat是线程不安全的,所以这里可以使用ThreadLocal隔离
例2:比如JDBC连接数据库,多线程环境公用Connection造成的线程不安全问题,也可以使用ThreadLocal处理。
【知识扩展】父线程生成的变量需要传递到子线程中进行使用的场景,可以了解InheritableThreadLocal