線程共享進(jìn)程的內(nèi)存空間段标,因此線程之間的變量是共享的瘩扼。ThreadLocal為線程提供一個(gè)私有空間焚碌,用于存儲(chǔ)私有變量兰怠。ThreadLocal是如何做到提供私有空間的呢。Read the fucking source code
ThreadLocal 有個(gè)靜態(tài)內(nèi)部類李茫,ThreadLocalMap揭保。從類名可知,該靜態(tài)內(nèi)部類是用于存儲(chǔ)數(shù)據(jù)使用魄宏,該靜態(tài)內(nèi)部類也有一個(gè) Entry的靜態(tài)內(nèi)部類秸侣,Entry繼承自WeakReference〕杌ィ看到WeakReference我們就能斷定味榛,這個(gè)Entry是存儲(chǔ)數(shù)據(jù)的單元,并且使用了虛引用予跌,便于垃圾回收器對(duì)其進(jìn)行回收搏色。
Entry類的泛型參數(shù)為ThreadLocal,可知該類持有ThreadLocal實(shí)例券册。Entry的構(gòu)造函數(shù)接收兩個(gè)參數(shù)频轿,一個(gè)為ThreadLocal 對(duì)象,另一個(gè)為Object對(duì)象汁掠。繼續(xù)看如何使用Entry略吨。
ThreadLocalMap構(gòu)造器接收兩個(gè)參數(shù),剛好和Entry一樣考阱。構(gòu)造方法里面翠忠,創(chuàng)建了初始大小的Entry數(shù)組,并創(chuàng)建了一個(gè)Entry對(duì)象乞榨,賦值給table數(shù)組里的某一個(gè)秽之。
看一下getEntry方法
該方法根據(jù)key的threadLocalHashCode與table長(zhǎng)度,尋找到存放在數(shù)組里的位置吃既。如果失敗考榨,則通過getEntryAfterMiss尋找Entry。具體是如何放置和尋找在table中的位置鹦倚,由于涉及hashCode河质,以后有機(jī)會(huì)再分析。我們記住通過ThreadLocal對(duì)象尋找到對(duì)應(yīng)的Entry震叙。
從firstKey掀鹅,firstValue的字段名稱,我們可以猜測(cè)媒楼,ThreadLocalMap接收key為ThreadLocal對(duì)象乐尊,值為Object對(duì)象的鍵值對(duì),存儲(chǔ)在Entry數(shù)組中划址,該類沒有繼承自Map類扔嵌,但也實(shí)現(xiàn)了與之類似的功能限府。接下來我們繼續(xù)看ThreadLocal是如何使用ThreadLocalMap的。
從ThreadLocal的get方法我們可以看到痢缎。先是獲取當(dāng)前線程胁勺,然后通過getMap方法獲取ThreadLocalMap對(duì)象,如果ThreadLocalMap對(duì)象存在牺弄,則通過map.getEntry(this)獲取Entry對(duì)象姻几,然后返回放置其中的value。如果ThreadLocalMap對(duì)象不存在則調(diào)用setInitialValue方法势告,創(chuàng)建ThreadLocalMap對(duì)象蛇捌,返回initialValue。
繼續(xù)查看getMap得知咱台,ThreadLocalMap是存放在Thread的threadLocals變量中络拌,而threadLocals變量是在createMap的時(shí)候,創(chuàng)建的一個(gè)ThreadLocalMap實(shí)例回溺。
總結(jié)一下:
每一個(gè)線程通過threadLocals字段萍恕,存放ThreadLocalMap對(duì)象,這個(gè)threadLocals字段為線程提供了私有空間车要≡试粒可以通過ThreadLocal實(shí)例,獲取與當(dāng)前線程關(guān)聯(lián)的ThreadLocalMap對(duì)象翼岁,從而獲取存放的value类垫。
1.通過同一個(gè)ThreadLocal實(shí)例,在不同線程中set琅坡,get悉患,存取在各自線程中的值。
2.通過不同的ThreadLocal實(shí)例榆俺,在同一線程的ThreadLocalMap中售躁,存放數(shù)據(jù)在Entry數(shù)組的不同位置。