ThreadLocalRandom類是JDK 7在JUC包下新增的隨機數生成器,它彌補了Random類在多執行緒下的缺陷。本文講解為何要在JUC下新增該類,以及該類的實現原理。一、Random類及其侷限性
2021-06-11 15:07:46
ThreadLocalRandom類是JDK 7在JUC包下新增的隨機數生成器,它彌補了Random類在多執行緒下的缺陷。本文講解為何要在JUC下新增該類,以及該類的實現原理。
一、Random類及其侷限性
在JDK 7之前包括現在,java.util.Random 都是使用比較廣泛的隨機數生成工具類,
而且java.lang.Math中的隨機數生成也使用的是java.util.Random的例項。下面先看看java.
util. Random的使用方法。
程式碼(1)創建一個預設隨機數生成器,並使用預設的種子。
程式碼(2)輸出10個在0~5 (包含0,不包含5)之間的隨機數。
隨機數的生成需要-一個預設的種子,這個種子其實是--個long類型的數字,你可以在
創建Random物件時通過建構函式指定,如果不指定則在預設建構函式內部生成一個預設
的值。有了預設的種子後,如何生成隨機數呢?
二、ThreadL ocalRandom
為了彌補多執行緒高併發情況下Random的缺陷,在JUC包下新增了ThreadL ocalRandom
類。下面首先看下如何使用它。
三、源碼分析
從圖中可以看出ThreadLocalRandom類繼承了Random類並重寫了nextInt方法,在ThreadLocalRandom類中並沒有使用繼承自Random類的原子性種子變數。在ThreadL,ocalRandom中並沒有存放具體的種子,具體的種子存放在具體的呼叫執行緒的threadL ocalRandomSeed變數裡面。ThreadI ocalRandom類似於ThreadLocal類,就是個工具類。當執行緒呼叫ThreadLocalRandom的current方法時,ThreadLocalRandom 負責初始化呼叫執行緒的threadLocalRandomSeed變數,也就是初始化種子。
當呼叫ThreadLocalRandom的nextInt方法時,實際上是獲取當前執行緒的threadL ocalRandomSeed變數作為當前種子來計算新的種子,然後更新新的種子到當前執行緒的threadLocalRandomSeed變數,而後再根據新種子並使用具體演算法計算隨機數。這裡需要注意的是,threadLocalRandomSeed 變數就是Thread類裡面的一個普通long變數,它並不是原子性變數。其實道理很簡單,因為這個變數是執行緒級別的,所以根本不需要使用原子性變數,如果你還是不理解可以思考下ThreadLocal的原理。其中seeder和probeGenerator是兩個原子性變數,在初始化呼叫執行緒的種子和探針變數時會用到它們,每個執行緒只會使用一次。
另外,變數instance是ThreadI ocalRandom的一個例項,該變數是static 的。當多執行緒通過ThreadLocalRandom的current方法獲取ThreadLocalRandom的例項時,其實獲取的是同一個例項。但是由於具體的種子是存放線上程裡面的,所以在ThreadLocalRandom的例項裡面只包含與執行緒無關的通用演算法,所以它是執行緒安全的。
下面看看ThreadLocalRandom的主要程式碼的實現邏輯。
1.Unsafe機制
2. ThreadL ocalRandom current()方法
該方法獲取ThreadL ocalRandom例項,並初始化呼叫執行緒中的threadL ocalRandomSeed
和threadLocalRandomProbe變數。
在如上程式碼(12)中,如果當前執行緒中threadL ocalRandomProbe的變數值為0 (預設情況下執行緒的這個變數值為0),則說明當前執行緒是第一次 呼叫ThreadLocalRandom的current方法,那麼就需要呼叫locallnit方法計算當前執行緒的初始化種子變數。這裡為了延遲初始化,在不需要使用隨機數功能時就不初始化Thread類中的種子變數,這是--種優化。
程式碼(13)首先根據probeGenerator計算當前執行緒中threadL ocalRandomProbe的初始化值,然後根據seeder計算當前執行緒的初始化種子,而後把這兩個變數設定到當前執行緒。程式碼(14) 返回ThreadLocalRandom的例項。需要注意的是,這個方法是靜態方法,多個執行緒返回的是同一個ThreadI ocalRandom例項。
3. int nextlnt(int bound)方法
計算當前執行緒的下一個隨機數。
如.上程式碼的邏輯步驟與Random相似,我們重點看下nextSeed0方法。
在如上程式碼中,首先使用r= UNSAFE.getLong(t, SEED)獲取當前執行緒中threadLocalRandomSeed變數的值,然後在種子的基礎上累加GAMMA值作為新種子,而後使用UNSAFE的putI ong方法把新種子放入當前執行緒的threadI ocalRandomSeed變數中。
總結:本文章首先講解了Random的實現原理以及Random在多執行緒下需要競爭種子原子變數更新操作的缺點,從而引出ThreadLocalRandom 類。ThreadL ocalRandom使用ThreadLocal的原理,讓每個執行緒都持有一個本地的種子變數,該種子變數只有在使用隨機數時才會被初始化。在多執行緒下計算新種子時是根據自己執行緒內維護的種子變數進行更新,從而避免了競爭。
相關文章
ThreadLocalRandom類是JDK 7在JUC包下新增的隨機數生成器,它彌補了Random類在多執行緒下的缺陷。本文講解為何要在JUC下新增該類,以及該類的實現原理。一、Random類及其侷限性
2021-06-11 15:07:46
5月6日,在OPPO K9系列釋出會期間,OPPO一款超高性價比無線耳機OPPO Enco Air正式來襲,售價299元,分別有被表「白」、敢自「黑」、正年「青」、有點「藍」四種配色,滿足不同使用
2021-06-11 15:06:40
作為一個需要照顧家庭一日三餐的家庭煮婦,每天的下廚時光既單一也無趣;日復一日的家常小炒也常常遭到家人的「嫌棄」,每天的飲食問題逐漸成為煮婦們日常生活困擾。怎麼樣才能讓
2021-06-11 15:06:03
不知什麼時候開始,「電話」APP 的開啟頻率越來越低,很多人甚至把它收納在位置隱蔽的資料夾裡。(桌面底欄,一個小而美就夠了)大部分社交需求,透過文字聊天,或者語音訊息就能解決。就
2021-06-11 14:47:27
尼康宣佈推出一新款雙筒望遠鏡——ACULON T02,此型號適合想要在室內和室外隨時都能享受到觀察樂趣的入門級/休閒型使用者使用。近年來,緊湊、輕量在整個雙筒望遠鏡市場上甚為
2021-06-11 14:46:37
今日訊息,滴滴已正式向美國證監會遞交了IPO招股書,股票程式碼為「DIDI」,高盛、摩根士丹利、摩根大通、華興資本擔任承銷商,計劃最終於紐交所掛牌上市。招股書的公佈,讓這家備受
2021-06-11 14:46:31