java并發(fā)高的情況下用ThreadLocalRandom來生成隨機(jī)數(shù)
一:簡(jiǎn)述
如果我們想要生成一個(gè)隨機(jī)數(shù),通常會(huì)使用Random類。但是在并發(fā)情況下Random生成隨機(jī)數(shù)的性能并不是很理想,今天給大家介紹一下JUC包中的用于生成隨機(jī)數(shù)的類--ThreadLocalRandom.(本文基于JDK1.8)
二:Random的性能差在哪里
Random隨機(jī)數(shù)生成是和種子seed有關(guān),而為了保證線程安全性,Random通過CAS機(jī)制來保證線程安全性。從next()方法中我們可以發(fā)現(xiàn)seed是通過自旋鎖和CAS來進(jìn)行修改值的。如果在高并發(fā)的場(chǎng)景下,那么可能會(huì)導(dǎo)致CAS不斷失敗,從而導(dǎo)致不斷自旋,這樣就可能會(huì)導(dǎo)致服務(wù)器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的簡(jiǎn)單使用
使用的方法很簡(jiǎn)單,通過ThreadLocalRandom.current()獲取到ThreadLocalRandom實(shí)例,然后通過nextInt(),nextLong()等方法獲取一個(gè)隨機(jī)數(shù)。
代碼:
@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); }
運(yùn)行結(jié)果:
四:為什么ThreadLocalRandom能在保證線程安全的情況下還能有不錯(cuò)的性能
我們可以看一下ThreadLocalRandom的代碼實(shí)現(xiàn)。
首先我們很容易看出這是一個(gè)餓漢式的單例
/** 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屬性的內(nèi)存偏移量,SEED成員變量代表的是Thread類的threadLocalRandomSeed屬性的內(nèi)存偏移量,SECONDARY成員變量代表的是Thread類的threadLocalRandomSecondarySeed屬性的內(nèi)存偏移量。
// 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類中確實(shí)有這三個(gè)屬性
Thread類:
@sun.misc.Contended("tlr") //當(dāng)前Thread的隨機(jī)種子 默認(rèn)值是0 long threadLocalRandomSeed; /** Probe hash value; nonzero if threadLocalRandomSeed initialized */ @sun.misc.Contended("tlr") //用來標(biāo)志當(dāng)前Thread的threadLocalRandomSeed是否進(jìn)行了初始化 0代表沒有,非0代表已經(jīng)初始化 默認(rèn)值是0 int threadLocalRandomProbe; /** Secondary seed isolated from public ThreadLocalRandom sequence */ @sun.misc.Contended("tlr") //當(dāng)前Thread的二級(jí)隨機(jī)種子 默認(rèn)值是0 int threadLocalRandomSecondarySeed;
接下來我們看ThreadLocalRandom.current()方法。
ThreadLocalRandom.current()
ThreadLocalRandom.current()的作用主要是初始化隨機(jī)種子,并且返回ThreadLocalRandom的實(shí)例。
首先通過UNSAFE類獲取當(dāng)前線程的Thread對(duì)象的threadLocalRandomProbe屬性,看隨機(jī)種子是否已經(jīng)初始化。沒有初始化,那么調(diào)用localInit()方法進(jìn)行初始化
public static ThreadLocalRandom current() { // 獲取當(dāng)前線程的 if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0) localInit(); return instance; }
localInit()
localInit()方法的作用就是初始化隨機(jī)種子,可以看到代碼很簡(jiǎn)單,就是通過UNSAFE類對(duì)當(dāng)前Thread的threadLocalRandomProbe屬性和threadLocalRandomSeed屬性進(jìn)行一個(gè)賦值。
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是如何生成到隨機(jī)數(shù)的。我們可以看出隨機(jī)數(shù)正是通過nextSeed()方法獲取到隨機(jī)種子,然后通過隨機(jī)種子而生成。所以重點(diǎn)看nextSeed()方法是如何獲取到隨機(jī)種子的。
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()方法的作用是獲取隨機(jī)種子,代碼很簡(jiǎn)單,就是通過UNSAFE類獲取當(dāng)前線程的threadLocalRandomSeed屬性,并且將原來的threadLocalRandomSeed加上GAMMA設(shè)置成新的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; }
小結(jié):
ThreadLocalRandom為什么線程安全?是因?yàn)樗鼘㈦S機(jī)種子保存在當(dāng)前Thread對(duì)象的threadLocalRandomSeed變量中,這樣每個(gè)線程都有自己的隨機(jī)種子,實(shí)現(xiàn)了線程級(jí)別的隔離,所以ThreadLocalRandom也并不需要像Random通過自旋鎖和cas來保證隨機(jī)種子的線程安全性。在高并發(fā)的場(chǎng)景下,效率也會(huì)相對(duì)較高。
注:各位有沒有發(fā)現(xiàn)ThreadLocalRandom保證線程安全的方式和ThreadLocal有點(diǎn)像呢
需要注意的點(diǎn):
1.ThreadLocalRandom是單例的。
2.我們每個(gè)線程在獲取隨機(jī)數(shù)之前都需要調(diào)用一下ThreadLocalRandom.current()來初始化當(dāng)前線程的隨機(jī)種子。
3.理解ThreadLocalRandom需要對(duì)UnSafe類有所了解,它是Java提供的一個(gè)可以直接通過內(nèi)存對(duì)變量進(jìn)行獲取和修改的一個(gè)工具類。java的CAS也是通過這個(gè)工具類來實(shí)現(xiàn)的。
到此這篇關(guān)于java并發(fā)高的情況下用ThreadLocalRandom來生成隨機(jī)數(shù)的文章就介紹到這了,更多相關(guān)java ThreadLocalRandom生成隨機(jī)數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)簡(jiǎn)易撲克牌游戲的完整實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)簡(jiǎn)易撲克牌游戲的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04SpringBoot項(xiàng)目攔截器獲取Post方法的請(qǐng)求body實(shí)現(xiàn)
本文主要介紹了SpringBoot項(xiàng)目攔截器獲取Post方法的請(qǐng)求body,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01Java多線程 兩階段終止模式Two-Phase Termination Patter
這篇文章主要介紹了Java多線程 兩階段終止模式Two-Phase Termination Patter,該模式有兩個(gè)角色,分別是Terminator,終止者,負(fù)責(zé)接收終止請(qǐng)求,執(zhí)行終止處理,處理完成后再終止自己。TerminationRequester終止請(qǐng)求發(fā)出者,用來向Terminator發(fā)出終止請(qǐng)求,需要的朋友可以參考一下2021-10-10Java中BeanUtils.copyProperties基本用法與小坑
本文主要介紹了Java中BeanUtils.copyProperties基本用法與小坑,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04java?集合工具類Collections及Comparable和Comparator排序詳解
這篇文章主要介紹了java集合工具類Collections及Comparable和Comparator排序詳解,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-06-06SpringBoot QQ郵箱發(fā)送郵件實(shí)例代碼
大家好,本篇文章主要講的是SpringBoot QQ郵箱發(fā)送郵件實(shí)例代碼,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12openEuler?搭建java開發(fā)環(huán)境的詳細(xì)過程
這篇文章主要介紹了openEuler?搭建java開發(fā)環(huán)境,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06java去除重復(fù)對(duì)象的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)硪黄猨ava去除重復(fù)對(duì)象的簡(jiǎn)單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-01-01