這次想總結ThreadLocal這個東西,也是由於項目中使用到了它去幫助保存會話信息。傳統的(或者說我在學校的時候)方法,大多是用服務端的session保存會話,與瀏覽器端的cookie協作去追蹤這個會話。而現在更多的使用ThreadLocal去保存會話的信息,這是由於ThreadLocal天生帶有線程安全的特性,並且僅僅在一個線程內共享變量(恰好符合多用戶多會話請求這一場景),這就使得這種方式用起來十分順手和簡單。
1. 項目中的使用:
首先記錄一下項目中ThreadLocal是如何保存用戶會話信息的:
1 private final String KEY_USER = "user"; 2 private final String KEY_TICKET = "ticket"; 3 private final String KEY_ROLEKEY = "roleKey"; 4 5 private static class Inner{ 6 static LocalThreadUtils localThreadUtils = new LocalThreadUtils(); 7 } 8 9 public static LocalThreadUtils getInstance(){ 10 return Inner.localThreadUtils; 11 } 12 13 14 private ThreadLocal<Map<String, Object>> threadLocal = ThreadLocal.withInitial(() -> Maps.newHashMap()); 15 16 17 public void cleanContext(){ 18 threadLocal.get().clear(); 19 }
核心就是這樣一個內部類的單例模式,產生的單例有一個theadLocal私有變量,用ThreadLocal修飾,內部是一個Map,而這個map可以由我們自己去存放用戶的各種信息,比如ticket,角色role,用戶信息user等。
ThreadLocal這種方式的優點在於實現簡單,並不需要我們在每一層傳遞request,也不用考慮httpSession的作用域,我們可以再任意層(controller,service等)直接訪問當前線程信息,從而取出會話信息。
2. 實現方式和原理:
ThreadLocal類是如何為每個線程創建一個變量的副本的:
首先,在每個線程Thread內部有一個ThreadLocal.ThreadLocalMap類型的成員變量threadLocals,這個threadLocals就是用來存儲實際的變量副本的,鍵值為當前ThreadLocal變量,value為變量副本(即T類型的變量)。
初始時,在Thread里面,threadLocals為空,當通過ThreadLocal變量調用get()方法或者set()方法,就會對Thread類中的threadLocals進行初始化,並且以當前ThreadLocal變量為鍵值,以ThreadLocal要保存的副本變量為value,存到threadLocals。
然后在當前線程里面,如果要使用副本變量,就可以通過get方法在threadLocals里面查找。
其他想要說的:
ThreadLocal真的不是用來解決對象共享訪問問題的,而主要是提供了保持對象的方法和避免參數傳遞的方便的對象訪問方式。
1、每個線程中都有一個自己的ThreadLocalMap類對象,可以將線程自己的對象保持到其中,各管各的,線程可以正確的訪問到自己的對象。
2、將一個共用的ThreadLocal靜態實例作為key,將不同對象的引用保存到不同線程的ThreadLocalMap中,然后在線程生命周期內執行的各處通過這個靜態ThreadLocal實例的get()方法取得自己線程保存的那個對象,避免了將這個對象作為參數傳遞的麻煩。
寫在最后:這里只是對ThreadLocal做一個小小的總結,因為在之前的項目中從來沒有真正使用過,只是字面上了解他的含義。很多知識確實要通過實踐才能有更深刻的理解。