理解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芋頭和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。
相關文章
相關標籤/搜索