一般的Web應用划分為展現層、服務層和持久層三個層次,在不同的層中編寫對應的邏輯,下層通過接口向上層開放功能調用。在一般情況下,從接收請求到返回響應所經過的所有程序調用都同屬於一個線程。
也就是說,同一線程貫通N層,不同的線程可能由於參數等不同會對程序中的某些變量進行修改,但是又要防止修改后的值對其它線程產生影響,因為不同的線程可以同時運行滴,這就需要我們解決對某些線程共享的變量的訪問沖突問題。ThreadLocal本地線程變量就是一種解決方式,它通過將程序中不安全的變量封裝進ThreadLocal中,這相當於為每一個線程提供一個獨立的變量副本(其實是不同的對象),線程修改變量的值對其它線程來說沒影響了,因為其它線程有自己的一個副本信息。


代碼理解:
1 // 借助ThreadLocal對象每個線程只創建一個實例 2 public static final String dateFormat="yyyy-MM-dd"; 3 4 private static final ThreadLocal<DateFormat> dfThreadLocal=new ThreadLocal<DateFormat>(){ 5 @Override 6 protected DateFormat initialValue() { 7 return new SimpleDateFormat(dateFormat); 8 } 9 }; 10 11 public static String dateToString(Date date){ 12 return dfThreadLocal.get().format(date); 13 }
對於每個線程,都有一個類似於Map的東西ThreadLocalMap(ThreadLocal的靜態類 ),那它里面保存了什么東東呢,肯定是key-value啊,key就是上面代碼中的共享靜態變量 dfThreadLocal,value就是DateFormat實例了,即new SimpleDateFormat(dateFormat)這個東東。那接下來,在線程內我要如何去獲取這個值呢,就是靠dfThreadLocal.get()實現滴,方法源碼如下:
1 ThreadLocal .ThreadLocalMap inheritableThreadLocals = null ; 2 3 public T get () { 4 Thread t = Thread.currentThread (); 5 ThreadLocalMap map = getMap(t ); 6 if ( map != null) { 7 ThreadLocalMap.Entry e = map.getEntry (this); 8 if ( e != null) 9 return ( T)e .value; 10 } 11 return setInitialValue (); 12 } 13 14 ThreadLocalMap getMap (Thread t) { 15 return t .inheritableThreadLocals; 16 }
可以很明顯的看出,
首先根據Thread.currentThread ()獲取到inheritableThreadLocals(即ThreadLocalMap,他是Thread的一個變量),然后將this(即最上面代碼的dfThreadLocal對象)作為key(或索引)獲取到真正的值T(就是SimpleDateFormat對象)啊,至此應該比較清楚了。
為什么不同的線程有各自的值,因為
不同的線程--->不同的ThreadLocalMap對象(線程的變量)--->通過相同的key(如果有被static修飾)獲取到不同的value值。
備注:一般都被static修飾,因為可以避免在一個線程內可能發生的重復創建TSO(Thread Specific Object,即ThreadLocal所關聯的對象),被statis修飾了,同一線程key也肯定一樣,value也肯定只有一份了。
一個ThreadLocal實例關聯當前線程的一個TSO對象,如果把ThreadLocal聲明為實例變量,那么每創建一個類實例就會導致一個TSO實例誕生,這肯定沒有這個必要滴。
更多文章請見我的個人博客:
http://www.acanblog.com