淘先锋技术网

首页 1 2 3 4 5 6 7

在没看过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