新四季網

阿里標籤識別教程(阿里開發手冊為什麼不建議使用Random)

2023-04-23 18:49:22 1

Random的使用

Random random = new Random; for (int i=0;i<10;i ){ System.out.println(random.nextInt(10)); }

那麼Random有什麼缺點呢?我們先看nextInt的源碼?

protected int next(int bits) { long oldseed, nextseed; AtomicLong seed = this.seed; do { //獲取當前原子變量種子的值 //(6) oldseed = seed.get; //根據當前種子值計算新的種子 //(7) nextseed = (oldseed * multiplier addend) & mask; //(8) //使用CAS 操作,它使用新的種子去更新老的種子,在多線程環境下 可能多個線程都同時 } while (!seed.compareAndSet(oldseed, nextseed)); //(9) return (int)(nextseed >>> (48 - bits)); }

(6) 獲取當前的原子變量種子的值

(7) 根據當前的種子值計算新的種子值

(8) 使用CAS 操作去更新舊的種子,在多線程的環境下可能會有多個線程同時執行(6)代碼,就有可能多線程拿到的是同一個值,然後多個線程再同時執行(7),又得到同一個最新的種子,但是步驟(8)的CAS操作會保證只有一個線程可以更新老的種子為新的,那麼失敗的線程怎麼辦呢?失敗的線程會通過循環獲取更新後的種子作為當前的 種子去計算新的種子,這就保證了隨機數的隨機性。

代碼(9) 使用固定算法根據新的種子計算隨機數。

總結:

在每個Random實例裡面都有一個原子性的變量用來記錄當前 的種子性,當要生成新的隨機數 時需要根據當前種子計算新的種子並更新回原子變量。在多線程下使用單個Random實例生成隨機數時,當多個線程同時計算隨機數來計算新的種子時,多個線程會競爭同一個原子變量的更新操作,由於原子變量的更新是CAS操作,同時只有一個線程會成功,所以會造成大量線程進行自旋重試,這回降低並發性能,所以ThreadLocalRandom應運而生。

Random的缺點是多個線程會使用同一個原子性種子變量,從而導致原子變量更新的競爭。

怎麼解決Random的缺點呢 ?

如果每個線程都維護一個種子變量,那麼每個線程生成隨機數時都是根據自己老的種子計算新的種子,再根據新的種子計算隨機數,那麼就不會存在競爭問題了,這會大大提高並發性能。ThreadLocalRandom原理如下圖:

怎麼使用呢?只需要使用下面代碼就可以了

ThreadLocalRandom.current.nextX(...)}

ThreadLocalRandom.current 代碼含義是 返回當前實例,並初始化此線程的種子。

ThreadLocalRandom是單例的。

public static ThreadLocalRandom current { if (UNSAFE.getInt(Thread.currentThread, PROBE) == 0) localInit; return instance; }

/** The common ThreadLocalRandom */static final ThreadLocalRandom instance = new ThreadLocalRandom;

我們看下localInit方法的實現:

static final void localInit { int p = probeGenerator.addAndGet(PROBE_INCREMENT); int probe = (p == 0) ? 1 : p; // skip 0 long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT)); Thread t = Thread.currentThread; UNSAFE.putLong(t, SEED, seed); UNSAFE.putInt(t, PROBE, probe);}

上面代碼是給每個線程分配一個種子;

我們繼續看next方法

public int nextInt(int bound) { if (bound <= 0) throw new IllegalArgumentException(BadBound); int r = mix32(nextSeed); int m = bound - 1; if ((bound & m) == 0) // power of two r &= m; else { // reject over-represented candidates for (int u = r >>> 1; u m - (r = u % bound) >> 1) ; } return r; }

如上代碼中,首先使用 ` r = UNSAFE.getLong(t, SEED) GAMMA` 獲取當前線程中的

threadLocalRandomSeed變量的值,然後在種子的基礎上累加GAMMA值作為新種子,而後使用UNSAFE的putLong方法把新種子放入當前線程的threadLocalRandomSeed變量中。

總結:

`ThreadLocalRandom` 使用ThreadLocal的原理,讓每個線程都持有一個本地的種子變量,該種子變量只有在使用隨機數時才會被初始化。在多線程計算新種子時是根據自己線程內維護的種子變量進行更新,從而避免了競爭。

最後建議:

在 JDK7 之後,可以直接使用 API ThreadLocalRandom,而在 JDK7 之前,需要編碼保證每個線程持有一個單獨的 Random 實例

,
同类文章
葬禮的夢想

葬禮的夢想

夢見葬禮,我得到了這個夢想,五個要素的五個要素,水火只好,主要名字在外面,職業生涯良好,一切都應該對待他人治療誠意,由於小,吉利的冬天夢想,秋天的夢是不吉利的
找到手機是什麼意思?

找到手機是什麼意思?

找到手機是什麼意思?五次選舉的五個要素是兩名士兵的跡象。與他溝通很好。這是非常財富,它擅長運作,職業是仙人的標誌。單身男人有這個夢想,主要生活可以有人幫忙
我不怎麼想?

我不怎麼想?

我做了什麼意味著看到米飯烹飪?我得到了這個夢想,五線的主要土壤,但是Tu Ke水是錢的跡象,職業生涯更加真誠。他真誠地誠實。這是豐富的,這是夏瑞的巨星
夢想你的意思是什麼?

夢想你的意思是什麼?

你是什​​麼意思夢想的夢想?夢想,主要木材的五個要素,水的跡象,主營業務,主營業務,案子應該抓住魅力,不能疏忽,春天夢想的吉利夢想夏天的夢想不幸。詢問學者夢想
拯救夢想

拯救夢想

拯救夢想什麼意思?你夢想著拯救人嗎?拯救人們的夢想有一個現實,也有夢想的主觀想像力,請參閱週宮官方網站拯救人民夢想的詳細解釋。夢想著敵人被拯救出來
2022愛方向和生日是在[質量個性]中

2022愛方向和生日是在[質量個性]中

[救生員]有人說,在出生88天之前,胎兒已經知道哪天的出生,如何有優質的個性,將走在什麼樣的愛情之旅,將與生活生活有什么生活。今天
夢想切割剪裁

夢想切割剪裁

夢想切割剪裁什麼意思?你夢想切你的手是好的嗎?夢想切割手工切割手有一個真正的影響和反應,也有夢想的主觀想像力。請參閱官方網站夢想的細節,以削減手
夢想著親人死了

夢想著親人死了

夢想著親人死了什麼意思?你夢想夢想你的親人死嗎?夢想有一個現實的影響和反應,還有夢想的主觀想像力,請參閱夢想世界夢想死亡的親屬的詳細解釋
夢想搶劫

夢想搶劫

夢想搶劫什麼意思?你夢想搶劫嗎?夢想著搶劫有一個現實的影響和反應,也有夢想的主觀想像力,請參閱週恭吉夢官方網站的詳細解釋。夢想搶劫
夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂什麼意思?你夢想缺乏異常藥物嗎?夢想缺乏現實世界的影響和現實,還有夢想的主觀想像,請看官方網站的夢想組織缺乏異常藥物。我覺得有些東西缺失了