準備工作
建立一個共享變量ThreadLocal
再建立一個向共享變量ThreadLocal 中指派的線程
再建立一個從共享變量ThreadLocal 中取值的線程
如下圖所示:tl為共享變量ThreadLocal
TestThreadForSet 為指派線程;TestThreadForGet 為取值線程,
為保證指派線程先于取值線程執行,中間加入了等待2秒
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL90TQORzaU1keJRVTx50MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL2QTNzIDMygTMxMDOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
運作結果:
如果ThreadLocal 為普通的共享變量,那麼取到的值一定為指派線程中設定的值。
然而:實際運作結果如下:
下面來說明為什麼會這樣:
首先我們需要了解指派線程指派的時候,把值存儲到哪裡去了?
我們跟蹤下ThreadLocal的set方法
1:首先擷取目前線程,然後執行了getMap方法,參數為目前線程,我們下面看getMap方法
這裡是重點,直接傳回的是線程内部的執行個體變量threadLocals,注意這裡是線程内部的執行個體變量,也就是說實際運作過程中每個線程都有一個threadLocals變量,這個變量的類型ThreadLocalMap。如下圖
2:getMap執行完後,我們再傳回到set方法中,這個時候會判斷map是否為空,因為我們是第一次指派,是以map肯定為null
,然後執行createMap方法,方法參數為目前線程對象,以及你要指派的變量。
3:我們看到這裡給線程的threadLocals變量指派了一個新的對象,這裡再次聲明一下,threadLocals是目前線程的執行個體變量,每個線程不共享,即如果有10個線程那麼就會有10個threadLocals變量,每個線程一個。下面我們看new ThreadLocalMap(this, firstValue);做了什麼,這個構造方法中第一個參數為this,這個this指向的是threadLocal變量,也就是在main方法中聲明的變量tl,看下圖
,
注意跟上面的threadLocals區分開;這個構造方法中第二個參數為你指派的内容。
4:上圖中構造方法裡的内容,這裡總結成一句話就是 以Threadlocal(這裡沒有s)變量對象為key,以你指派的内容為value放到ThreadLocalMap中。
總結一下,下圖中這幾條語句的主要邏輯是,首先擷取目前線程中的一個執行個體變量map,如果擷取到的map為空,那麼就建立一個map,并把ThreadLocal變量作為key,自定義指派内容為value放到map中,如果不為空,那就直接放了,相信很多同學都寫過這樣的邏輯。
到此為止,我們講了set方法的邏輯。下面我們看下get方法的邏輯,get方法的邏輯其實就是到目前線程的threadLocals變量(ThreadLocalMap類型)裡找this為key對應的value,this變量指的是ThreadLocal共享變量tl。注意這裡又提到了目前線程。
我們用一張圖總結一下上面的内容