首页 > 技术文章 > 理解ThreadLocal

maypattis 2016-03-24 23:21 原文

    在使用Spring MVC开发web项目时,在一个请求的任意阶段,都可以通过RequestContextHolder.getRequestAttributes()获取RequsetAttributes对象,进而获取request对象。这是怎么实现的呢?带着这个疑问,我们一起理解一下ThreadLocal对象。

    首先看一下getRequestAttributes()的实现

public static RequestAttributes getRequestAttributes() {
   RequestAttributes attributes = requestAttributesHolder.get();
   if (attributes == null) {
      attributes = inheritableRequestAttributesHolder.get();
   }
   return attributes;
}

这里的requestAttributesHolder是RequestContextHolder类的一个成员变量,它的定义是
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<RequestAttributes>("Request attributes");

可见它是一个ThreadLocal对象。那么ThreadLocal对象是做什么的呢?一般我们称它本地线程变量,是一个变量在线程中存储的一个副本。我们不妨先看一下它的源代码。
  先看一下requestAttributesHolder.get()中用到的get()函数。代码如下
  
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

首先获取当前线程,然后getMap(t),这一步做了什么呢?看代码
 1 ThreadLocalMap getMap(Thread t) { 2 return t.threadLocals; 3 } 

即获取线程对象t中的成员变量threadLocals,这个变量在Thread对象中的定义为
ThreadLocal.ThreadLocalMap threadLocals = null;

我们接着上面往下看。当map!=null时,以当前ThreadLocal对象为key从map中取值。若map==null,则返回setInitialValue()。再看下
setInitialValue():
 1 private T setInitialValue() {
 2     T value = initialValue();
 3     Thread t = Thread.currentThread();
 4     ThreadLocalMap map = getMap(t);
 5     if (map != null)
 6         map.set(this, value);
 7     else
 8         createMap(t, value);
 9     return value;
10 }
protected T initialValue() {
    return null;
}


void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

 

至此,相信读者应该有一个大概的思路了。这里setInitialValue()做的事,先通过初始化方法获取初始化值,然后获取当前线程的threadLocals对象,若threadLocals不为空则为当前ThreadLocal
对象赋予初始值,否则新建一个threadLocals对象。

除了get()方法,还有一个set()方法。代码如下

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

 

 
同样先获取当前线程的成员变量threadLocals,若不为空则以当前对象为key赋值,若为空则新建一个threadLocals,通过构造器赋值。

这时候再看看文章一开始提到的requestAttributesHolder变量,它是一个static final对象,意味着它是一个全局常量,那么在当前线程中的任意位置,用requestAttributesHolder.get()

方法获取存储在Thread的threadLocals对象中的值,都是可以获取到想要的值的。
总结一下,ThreadLocal对象是为了在一个线程执行的任意时间内获取某个值而创建的变量副本,这个副本存在当前线程的一个成员变量threadLocals里,存储的数据结构类似于key-value,
只不过key为ThreadLocal对象而已。第一篇技术博客写的比较青涩,还望读者多多见谅。

参考资料

1. JDK7 源码
 
作者:mayday芋头
出处:http://www.cnblogs.com/maypattis/
本博客中未标明转载的文章归作者mayday芋头和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 

 


推荐阅读