理解ThreadLocal

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

    首先看一下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芋头
本博客中未标明转载的文章归做者mayday芋头和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文链接,不然保留追究法律责任的权利。
相关文章
相关标签/搜索