詳解ThreadLocal模式

ThreadLocal:java.lang.ThreadLocal
	作用:解決多線程中數據共享的問題,即:解決了同一線程中隸屬於不同開發層次(表示層、業務層、持久層)的數據共享問題,故可以對執行邏輯和執行數據進行有效的解耦
	應用:應用在同一個線程的不同開發層次中共享數據。例如:Struts2中的ActionContext、Spring中管理數據庫的連接、Hibernate中的Session
	實現:
		1)建立一個類,並在其中封裝一個靜態的ThreadLocal變量,使其成爲一個共享的數據環境。
		2)在類中實現訪問靜態ThreadLocal變量的靜態方法(設值和取值)

	【說明】:ThreadLocal通過操作當前線程中的一個內部變量(ThreadLocalMap)來達到與其它線程隔離的目的。
		1)ThreadLocalMap變量屬於線程的內部屬性,不同的線程擁有完全不同的ThreadLocalMap變量
		2)線程中的ThreadLocalMap變量的值是在ThreadLocal對象進行set或get操作時創建的,在創建之前會檢查當前線程中的ThreadLocalMap是否爲null,如果爲null則創建,如果不爲null,則使用已經存在的ThreadLocalMap
		3)使用當前線程的ThreadLocalMap的關鍵在於:使用當前的ThreadLocal的實例作爲key進行儲存
	【重要】:
		1)一個ThreadLocal只能儲存一個變量,如果重複調用ThreadLocal的set方法,則新值會將舊值覆蓋。
		2)如果需要在一個線程中共享多個變量,則可以將多個變量封裝到一個對象中,然後將該對象存儲在ThreadLocal中。Struts2中的ActionContext就是這樣設計的。
		3)在ThreadLocalMap中,如果當前線程中定義了多個不同的ThreadLocal的實例,則它們會作爲不同的key進行儲存而不會相互干擾!


	java.lang.Thread的部分源碼:
		public class Thread implements Runnable {
			//這裏省略了許多其它的代碼
			
			ThreadLocal.ThreadLocalMap threadLocals = null;
		}
		
	java.lang.ThreadLocal的部分源碼:
		public Class ThreadLocal<T> {
			//這裏省略了許多其它的代碼
			
			// 將value的值保存在當前線程的本地變量表(ThreadLocalMap)中
			public void set(T value){
				// 獲取當前線程
				Thread t = Thread.currentThread();
				// 調用getMap方法獲得當前線程中的本地變量表ThreadLocalMap
				ThreadLocalMap map = getMap(t);
				// 如果本地變量表ThreadLocalMap已經存在,則直接使用
				if (map != null){
					// 以當前的ThreadLocal的實例作爲key,儲存在當前線程中。ThreadLocalMap
					// ThreadLocalMap中,如果當前線程中定義了多個不同的ThreadLocal的實例,則它們會作爲不同的key進行儲存而不會相互干擾
					// ThreadLocalMap的set方法:private void set(ThreadLocal<?> key, Object value)
					map.set(this, value);
				} else {
					// 如果ThreadLocalMap不存在,則初始化map
					createMap(t, value);
				}	
			}
			
			// 獲取當前線程中以當前ThreadLocal實例爲key的變量值
			public T get() {
				// 獲取當前線程
				Thread t = Thread.currentThread();
				// 獲取當前線程中的ThreadLocalMap
				ThreadLocalMap map = getMap(t);
				if (map != null) {
					// 獲取當前線程中以當前ThreadLocal實例爲key的變量值
					ThreadLocalMap.Entry e = map.getEntry(this);
					if (e != null) {
						return (T)e.value;
					}
				}
				
				// 當map不存在時,初始化map,並返回null
				return setInitialValue();
			}
			
			// 從當前線程中獲取與之對應的ThreadLocalMap
			ThreadLocalMap getMap(Thread t) {
				return t.threadlocals;
			}
			
			// 創建當前線程中的ThreadLocalMap
			void createMap(Thread t, T firstValue) {
				// 調用構造函數生成當前線程中的ThreadLocalMap
				t.threadLocals = new ThreadLocalMap(this, firstValue);
			}
			
			// 初始化當前線程的ThreadLocalMap
			private T setInitialValue() {
				T value = initialValue();
				Thread t = Thread.currentThread();
				ThreadLocalMap map = getMap(t);
				if (map != null)
					map.set(this, value);
				else
					createMap(t, value);
				return value;
			}
			
			// 若當前線程的ThreadLocalMap不存在時,調用ThreadLocal的get()方法默認返回null。
			// 若子類覆蓋父類ThreadLocal的該方法,則可以設置當(當前線程的)ThreadLocalMap不存在時,get()方法默認返回的值。
			protected T initialValue() {
				return null;
			}

			
			// ThreadLocalMap的定義 
			static class ThreadLocalMap{
				// ...
			}
			
		}


	應用舉例:Struts2中的ActionContext
		
	ActionContext在內部封裝了一個靜態的ThreadLocal的實例,而這一實例操作的對象()又是ActionContext本身。
		
	Struts2中的ActionContext的部分源碼:	
		public class ActionContext implements Serializable {
			// 此處省略了很多代碼
			
			// 封裝了一個ThreadLocal變量,儲存的內容是ActionContext本身
			static ThreadLocal actionContext = new ThreadLocal();
			
			// 在ThreadLocal中設置ActionContext,綁定到當前線程。
			public static void setContext(ActionContext context){
				actionContext.set(context);
				// 注:ThreadLocal的set方法調用了ThreadLocalMap(當前線程的本地變量表)的set(threadLocal, value)方法:以當前的ThreadLocal的實例作爲key,儲存在當前線程的ThreadLocalMap屬性中
			}
			
			// 返回當前線程中儲存的ActionContext
			public static ActionContext getContext(){
				// 返回當前線程中儲存的ActionContext
				return (ActionContext) actionContext.get();
			}
			
		}



	

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章