<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
如果我們想要生成一個亂數,通常會使用Random類。但是在並行情況下Random生成亂數的效能並不是很理想,今天給大家介紹一下JUC包中的用於生成亂數的類--ThreadLocalRandom.(本文基於JDK1.8)
Random亂數生成是和種子seed有關,而為了保證執行緒安全性,Random通過CAS機制來保證執行緒安全性。從next()方法中我們可以發現seed是通過自旋鎖和CAS來進行修改值的。如果在高並行的場景下,那麼可能會導致CAS不斷失敗,從而導致不斷自旋,這樣就可能會導致伺服器CPU過高。
protected int next(int bits) { long oldseed, nextseed; AtomicLong seed = this.seed; do { oldseed = seed.get(); nextseed = (oldseed * multiplier + addend) & mask; } while (!seed.compareAndSet(oldseed, nextseed)); return (int)(nextseed >>> (48 - bits)); }
使用的方法很簡單,通過ThreadLocalRandom.current()獲取到ThreadLocalRandom範例,然後通過nextInt(),nextLong()等方法獲取一個亂數。
程式碼:
@Test void test() throws InterruptedException { new Thread(()->{ ThreadLocalRandom random = ThreadLocalRandom.current(); System.out.println(random.nextInt(100)); }).start(); new Thread(()->{ ThreadLocalRandom random = ThreadLocalRandom.current(); System.out.println(random.nextInt(100)); }).start(); Thread.sleep(100); }
執行結果:
我們可以看一下ThreadLocalRandom的程式碼實現。
首先我們很容易看出這是一個餓漢式的單例
/** Constructor used only for static singleton */ private ThreadLocalRandom() { initialized = true; // false during super() call } /** The common ThreadLocalRandom */ static final ThreadLocalRandom instance = new ThreadLocalRandom();
我們可以看到PROBE成員變數代表的是Thread類的threadLocalRandomProbe屬性的記憶體偏移量,SEED成員變數代表的是Thread類的threadLocalRandomSeed屬性的記憶體偏移量,SECONDARY成員變數代表的是Thread類的threadLocalRandomSecondarySeed屬性的記憶體偏移量。
// Unsafe mechanics private static final sun.misc.Unsafe UNSAFE; private static final long SEED; private static final long PROBE; private static final long SECONDARY; static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class<?> tk = Thread.class; SEED = UNSAFE.objectFieldOffset (tk.getDeclaredField("threadLocalRandomSeed")); PROBE = UNSAFE.objectFieldOffset (tk.getDeclaredField("threadLocalRandomProbe")); SECONDARY = UNSAFE.objectFieldOffset (tk.getDeclaredField("threadLocalRandomSecondarySeed")); } catch (Exception e) { throw new Error(e); } }
可以看到Thread類中確實有這三個屬性
Thread類:
@sun.misc.Contended("tlr") //當前Thread的隨機種子 預設值是0 long threadLocalRandomSeed; /** Probe hash value; nonzero if threadLocalRandomSeed initialized */ @sun.misc.Contended("tlr") //用來標誌當前Thread的threadLocalRandomSeed是否進行了初始化 0代表沒有,非0代表已經初始化 預設值是0 int threadLocalRandomProbe; /** Secondary seed isolated from public ThreadLocalRandom sequence */ @sun.misc.Contended("tlr") //當前Thread的二級隨機種子 預設值是0 int threadLocalRandomSecondarySeed;
接下來我們看ThreadLocalRandom.current()方法。
ThreadLocalRandom.current()
ThreadLocalRandom.current()的作用主要是初始化隨機種子,並且返回ThreadLocalRandom的範例。
首先通過UNSAFE類獲取當前執行緒的Thread物件的threadLocalRandomProbe屬性,看隨機種子是否已經初始化。沒有初始化,那麼呼叫localInit()方法進行初始化
public static ThreadLocalRandom current() { // 獲取當前執行緒的 if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0) localInit(); return instance; }
localInit()
localInit()方法的作用就是初始化隨機種子,可以看到程式碼很簡單,就是通過UNSAFE類對當前Thread的threadLocalRandomProbe屬性和threadLocalRandomSeed屬性進行一個賦值。
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); }
接下來以nextInt()方法為例,看ThreadLocalRandom是如何生成到亂數的。我們可以看出亂數正是通過nextSeed()方法獲取到隨機種子,然後通過隨機種子而生成。所以重點看nextSeed()方法是如何獲取到隨機種子的。
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) < 0; u = mix32(nextSeed()) >>> 1) ; } return r; }
nextSeed()
nextSeed()方法的作用是獲取隨機種子,程式碼很簡單,就是通過UNSAFE類獲取當前執行緒的threadLocalRandomSeed屬性,並且將原來的threadLocalRandomSeed加上GAMMA設定成新的threadLocalRandomSeed。
final long nextSeed() { Thread t; long r; // read and update per-thread seed UNSAFE.putLong(t = Thread.currentThread(), SEED, r = UNSAFE.getLong(t, SEED) + GAMMA); return r; }
小結:
ThreadLocalRandom為什麼執行緒安全?是因為它將隨機種子儲存在當前Thread物件的threadLocalRandomSeed變數中,這樣每個執行緒都有自己的隨機種子,實現了執行緒級別的隔離,所以ThreadLocalRandom也並不需要像Random通過自旋鎖和cas來保證隨機種子的執行緒安全性。在高並行的場景下,效率也會相對較高。
注:各位有沒有發現ThreadLocalRandom保證執行緒安全的方式和ThreadLocal有點像呢
需要注意的點:
1.ThreadLocalRandom是單例的。
2.我們每個執行緒在獲取亂數之前都需要呼叫一下ThreadLocalRandom.current()來初始化當前執行緒的隨機種子。
3.理解ThreadLocalRandom需要對UnSafe類有所瞭解,它是Java提供的一個可以直接通過記憶體對變數進行獲取和修改的一個工具類。java的CAS也是通過這個工具類來實現的。
到此這篇關於java並行高的情況下用ThreadLocalRandom來生成亂數的文章就介紹到這了,更多相關java ThreadLocalRandom生成亂數內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45