ThreadLocal專題(二)— 數據倉庫機制

共圖社大白 大白共圖社  

 

 

思維一,構建Thread數據倉庫機制

今天介紹的角色是線程的數據倉庫羣和倉庫管理員:

 

1.數據倉庫管理人員:ThreadLocalMap

             

這個倉庫管理員維護的屬性:Entry數組 table,table數組的大小。

倉庫管理員的核心行爲如圖所示:

             

圖中反饋的信息可以知道:倉庫管理員維護着倉庫羣table,而getEntry,set,remove方法表示對table的行爲:倉庫內物品的獲取,物品的存放,物品的清除。

 

 

2.數據倉庫:Entry

             

倉庫管理員維護的數據倉庫羣table數組的元素類型就是Entry,這個類是個特殊的類,這裏不做進一步的剖析,會在以後文章內單獨提出來。

數據倉庫的角色Entry維護這一個屬性value,沒有其他行爲方法,所以倉庫內存放的物品只有一個,即value,是個object類型——可以放入java體系中的任何引用類型。

因此,ThreadLocal的數據倉庫可以放入任何類型的數據。

 

 

 

線程的數據倉庫機制

線程維護這數據倉庫的管理員角色,而且還有兩個管理員threadLocals和inheritableThreadLocals

            

這裏我們只談threadLocals這一個屬性,我們上面已經分析,threadLocals是一個數據倉庫羣管理員,管理着倉庫羣tables,方法有:set,getEntry,remove等。字段threadLocals在線程類中的訪問權限是默認修飾符,所以同一個包的類ThreadLocal可以訪問。

所以長工角色雖然是個打工的人,但是真正幹事的還是倉庫羣管理員ThreadLocalMap,因爲長工的搬運工作都是委託給管理人員來施行的,他只需要在倉庫外面等待,ThreadLocalMap類的get方法在ThreadLocal的get方法中,如下圖。

            

 

其他委託方式也和get類似,如下兩個圖。

            

            

 

 

 

線程的數據倉庫繼承機制

Thread的繼承機制,大家可能還沒碰到過,但是在java體系中卻十分重要,不過這裏的繼承機制不是java中的類繼承。這裏的繼承是指父子線程的屬性繼承以及數據倉庫羣的繼承。

 

子線程Thread被創建時會調用init()方法,這個方法是在父線程中運行,其中一行代碼

            

所以,parent是當前線程(父線程)的引用,所以父子線程的屬性繼承傳遞發生在init()方法內,下圖是線程組,守護線程,線程優先級等屬性的繼承代碼。

            

 

數據倉庫的繼承代碼如下圖,inheritableThreadLocals是用於數據倉庫繼承機制的屬性,ThreadLocalMap的引用。

            

圖中代碼顯示,如果父線程inheritableThreadLocals屬性不爲空,則會創建一個新的ThreadLocalMap對象給子線程對象,且管理的倉庫羣物品和父線程一樣。這就是線程的繼承機制。

 

 

長工ThreadLocal的工作

所有的線程都是公用一個ThreadLocal對象的,線程通過threadlocal來從線程內部數據倉庫羣中獲取數據,不言而喻,幾個線程調用同一個threadlocal返回的是不同數據,同樣set存放進去的數據也是使用同一個threadlocal的set方法。還有數據的移除和初始化,如下所示:

1.ThreadLocal.get:長工角色獲取數據

2.ThreadLocal:set:長工角色存放數據

3.ThreadLocal:remove:   長工角色移除數據

 

我們已經談到,倉庫的數據存放工作不是由長工角色Thread Local來完成的,而是委託給倉庫羣的管理員角色Thread LocalMap來執行的,分別對應ThreadLocalMap的方法,如下所示:

1.ThreadLocalMap.getEntry(ThreadLocal<?>key):

2.ThreadLocalMap.set(ThreadLocal<?>key,Object value):

3.ThreadLocalMap.remove(ThreadLocal<?>key);

也就是說ThreadLocal的三個方法get,set,remove都在方法體內調用了ThreadLocalMap的getEntry,set,remove方法,而這個ThreadLocalMap數據管理員角色對象是線程的字段,在ThreadLocal的三種方法中都需要去獲取當前線程的ThreadLocalMap對象,代碼如下所示:

            

三個方法裏面都有這段代碼:先獲取Thread LocalMap對象,即Thread Local將數據的搬運工作委託給了線程對象的屬性ThreadLocalMap。

 

對長工工作的小結:大兄弟角色線程將數據搬運工作委託給threadlocal長工角色,而長工找到屬於大兄弟的數據倉庫羣管理員threadlocalmap,將搬運的工作委託給管理員。

 

 

table中倉庫Entry的定位

我們現在已經知道線程對象 的數據放入的是數據倉庫table數組中,每個threadlocal對象獲取和存放操作是處理table數組中的哪個位置的數據,位置是如何定位的,這個就要涉及到ThreadLocal的其他機制,例如:ThreadLocal對象唯一ID機制,ThreadLocal的get初始化值機制等等。

 

 

上面段落提到數據的搬運工作最後是委託給了ThreadLocalMap這個對象,以get方法爲例,底層是管理員角色的getEntry方法在工作,如下圖所示:

            

threadLocalHashCode字段屬性是ThreadLoca對象唯一ID機制中的ID,表示這個對象的唯一性質,而這個i值也就因爲這個threadLocalHashCode的唯一性而具有唯一性。所以可以判斷具有唯一性的Threadlocal對象能獲得具有唯一性質的i,因此對應的table[i]也就唯一對應到ThreadLocal對象了。

 

對倉庫Entry定位的小結:因爲ThreadLocal對象的具有唯一性,因此它指向的是線程對象中數據倉庫羣table中的其中一個數據倉庫entry,哪個線程委託threadlocal對象,threadlocal就去哪個線程下的倉庫羣進行操作。這個小結能夠解決對ThreadLocal體系模糊不清的疑問,爲什麼不同線程調用同一個threadlocal對象的相同方法會獲取不一樣的數據?爲什麼不同的線程使用一個threadlocal對象進行set,還能保存兩種這樣的數據?

 

ThreadLocal數據倉庫機制,它不僅僅只涉及到ThreadLocal這一個角色類,它還與Thread角色類強關聯一起:數據就是維護在Thread對象中的。而數據存放位置的定位key包括:具有唯一行的ThreadLocal對象+Thread對象。到這裏就是我對於ThreadLocal數據倉庫機制的分享,感謝閱讀,希望能幫助到你,我是大白,我來自共圖社,一個持續輸出Java好文的平臺。

 

                      talk is cheap  ,show you the code and the doc,更多的分享內容請關注我的工作號:大白共圖社。公衆號會有很多的github開源社區拿來即用項目源碼以及相關的文章。歡迎關注。 

                                                                       

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