欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的Random和ThreadLocalRandom詳細(xì)解析

 更新時間:2024年01月27日 10:29:12   作者:魅Lemon  
這篇文章主要介紹了Java中的Random和ThreadLocalRandom詳細(xì)解析,Random 類用于生成偽隨機(jī)數(shù)的流, 該類使用48位種子,其使用線性同余公式進(jìn)行修改,需要的朋友可以參考下

一、Random類

1、簡介

Random 類用于生成偽隨機(jī)數(shù)的流。 該類使用48位種子,其使用線性同余公式進(jìn)行修改

  • Math.random()使用起來相對更簡單,但不是線程安全的;
  • java.util.Random的Random類是線程安全的。 但是跨線程的同時使用java.util.Random實(shí)例可能會遇到爭用,從而導(dǎo)致性能下降。 在多線程設(shè)計中考慮使用ThreadLocalRandom類;
  • java.util.Random的Random不是加密安全的。 考慮使用SecureRandom獲取一個加密安全的偽隨機(jī)數(shù)生成器,供安全敏感應(yīng)用程序使用

2、Random的構(gòu)造函數(shù)

Random():創(chuàng)建一個新的隨機(jī)數(shù)生成器

/**
 * Creates a new random number generator. This constructor sets
 * the seed of the random number generator to a value very likely
 * to be distinct from any other invocation of this constructor.
 */
public Random() {
    //System.nanoTime()返回正在運(yùn)行的Java虛擬機(jī)的高分辨率時間源的當(dāng)前值,以納秒為單位。
    //這里會調(diào)用有參構(gòu)造函數(shù)
    this(seedUniquifier() ^ System.nanoTime());
}
private static long seedUniquifier() {
    // 線性同余生成元表  
    for (;;) {
        long current = seedUniquifier.get();
        long next = current * 181783497276652981L; 
        // 兩個很大的數(shù)相乘
        if (seedUniquifier.compareAndSet(current, next))
            // 這個比較并且交換CAS
            // 比較當(dāng)前工作內(nèi)存中的值和主內(nèi)存中的值,如果這個值是期望的,那么則執(zhí)行操作!
            //如果不是就一直循環(huán)!就是為了保證即使在多線程的環(huán)境中返回的也是不同的數(shù)
            return next;
    }
}
// atomic 這個是 juc 里面修飾的原子性的 long ,get方法說就是獲得這個構(gòu)造函數(shù)里面的值
 private static final AtomicLong seedUniquifier
        = new AtomicLong(8682522807148012L);

random(long seed):使用單個 Long 種子創(chuàng)建一個新的隨機(jī)數(shù)生成器

偽隨機(jī)使用了線性同余法(具體可自行查閱資料)

public Random(long seed) {
    if (getClass() == Random.class)
        this.seed = new AtomicLong(initialScramble(seed));
    else {
        // 子類可能重寫了這個不考慮
        this.seed = new AtomicLong(); // 創(chuàng)建一個新的AtomicLong,初始值為 0 
        setSeed(seed);
    }
}
    //清除nextGaussian()使用的haveNextNextGaussian 標(biāo)志,
    synchronized public void setSeed(long seed) {
        this.seed.set(initialScramble(seed));
        haveNextNextGaussian = false; 
     }
    private static long initialScramble(long seed) {
        return (seed ^ multiplier) & mask;
     }
  private static final long multiplier = 0x5DEECE66DL;
  // x & [(1L << 48)–1]與 x(mod 2^48)等價 取低位48位
  // 帶符號左移
  private static final long mask = (1L << 48) - 1;

3、next()核心方法

Random在多線程的環(huán)境是并發(fā)安全的,它解決競爭的方式是使用用原子類,本質(zhì)上上也就是CAS + Volatile保證線程安全

在這里插入圖片描述

在Random類中,有一個AtomicLong的域,用來保存隨機(jī)種子。其中每次生成隨機(jī)數(shù)時都會根據(jù)隨機(jī)種子做移位操作以得到隨機(jī)數(shù)。

//Long類型的隨機(jī)
//long類型在Java中總弄64bit,對next方法的返回值左移32作為long的高位,然后將next方法返回值作為低32位,作為long類型的隨機(jī)數(shù)。
//此處關(guān)鍵之處在于next方法
public long nextLong() {
    return ((long)(next(32)) << 32) + next(32);
}

以下是next方法的核心,使用seed種子,不斷生成新的種子,然后使用CAS將其更新,再返回種子的移位后值。這里不斷的循環(huán)CAS操作種子,直到成功??梢?,Random實(shí)現(xiàn)原理主要是利用隨機(jī)種子采用一定算法進(jìn)行處理生成隨機(jī)數(shù),在隨機(jī)種子的安全保證利用原子類AtomicLong。

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));
    }

4、Random在并發(fā)下的缺點(diǎn)

雖然Random是線程安全,但是對于并發(fā)處理使用原子類AtomicLong在大量競爭時,使用同一個 Random 對象可能會導(dǎo)致線程阻塞,由于很多CAS操作會造成失敗,不斷的Spin,而造成CPU開銷比較大而且吞吐量也會下降。

這里可以自行多線程測試,可以發(fā)現(xiàn)隨著線程增加,Random隨著競爭越來越激烈,然后耗時越來越多。然而ThreadLocalRandom隨著線程數(shù)的增加,基本沒有變化。所以在大并發(fā)的情況下,隨機(jī)的選擇,可以考慮ThreadLocalRandom提升性能。

二、ThreadLocalRandom

1、簡介

ThreadLocalRandom是Random的子類,它是將Seed隨機(jī)種子隔離到當(dāng)前線程的隨機(jī)數(shù)生成器,從而解決了Random在Seed上競爭的問題,它的處理思想和ThreadLocal本質(zhì)相同。

Unsafe 類內(nèi)的方法透露著一股 “Unsafe” 的氣息,具體表現(xiàn)就是可以直接操作內(nèi)存,而不做任何安全校驗(yàn),如果有問題,則會在運(yùn)行時拋出 Fatal Error,導(dǎo)致整個虛擬機(jī)的退出。

2、原理分析

2.1 ThreadLocalRandom單例模式

從下述代碼可以發(fā)現(xiàn)ThreadLocalRandom使用了單例模式,即在一個Java應(yīng)用中只有一個ThreadLocalRandom對象。

當(dāng)UNSAFE.getInt(Thread.currentThread(), PROBE)返回0時,就執(zhí)行l(wèi)ocalInit(),最后返回單例。

// 一個java應(yīng)用只有一個實(shí)例
// ThreadLocalRandom對象通過ThreadLocalRandom.current()獲取,之后可以直接返回隨機(jī)數(shù)
static final ThreadLocalRandom instance = new ThreadLocalRandom();
//獲取ThreadLocalRandom對象實(shí)例
public static ThreadLocalRandom current() {
    if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
        localInit();
    return instance;
}

2.2 Seed隨機(jī)種子隔離到當(dāng)前線程

核心方法

//會從 object 對象var1的內(nèi)存地址偏移var2后的位置讀取四個字節(jié)作為long型返回
public native long getLong(Object var1, long var2);
//可以將object對象var1的內(nèi)存地址偏移var2后的位置后四個字節(jié)設(shè)置為 var4
public native void putLong(Object var1, long var2, long var4);

UNSAFE.getInt(Thread.currentThread(), PROBE)是獲取當(dāng)前Thread線程對象中的PROBE。

首先獲取變量名SEED、PROBE等參數(shù)相對對象的偏移位置

// 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);
    }
}

當(dāng)?shù)谝淮握{(diào)用ThreadLocalRandom.current()方法時當(dāng)前線程檢測到PROBE未初始化會調(diào)用localInit()方法進(jìn)行初始化,并把當(dāng)前線程的seed值和probe值存儲在當(dāng)前線程Thread對象內(nèi)存地址偏移相對應(yīng)變量的位置

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);
}

threadLocalRandomProbe用于表示當(dāng)前線程Thread是否初始化,如果是非0,表示其已經(jīng)初始化。

換句話說,該變量就是狀態(tài)變量,用于標(biāo)識當(dāng)前線程Thread是否被初始化。

threadLocalRandomSeed從注釋中也可以看出,它是當(dāng)前線程的隨機(jī)種子。

隨機(jī)種子分散在各個Thread對象中,從而避免了并發(fā)時的競爭點(diǎn)。

3、nextSeed()核心方法

nextSeed()生成隨機(jī)種子用來生成隨機(jī)數(shù)序列

public long nextLong() {
    return mix64(nextSeed());
}

因?yàn)樵诔跏蓟臅r候已經(jīng)存儲了當(dāng)前線程的seed值和probe值到相應(yīng)線程對象內(nèi)存地址的偏移位置,調(diào)用nextSeed()時直接從當(dāng)前線程對象偏移位置處進(jìn)行獲取,并生成下一個隨機(jī)數(shù)種子到該位置,同時使用了UNSAFE類方法,不同線程間不需要競爭獲得seed值,因此可以可以將競爭點(diǎn)隔離

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é)

Random是Java中提供的隨機(jī)數(shù)生成器工具類,但是在大并發(fā)的情況下由于其隨機(jī)種子的競爭會導(dǎo)致吞吐量下降,從而引入ThreadLocalRandom它將競爭點(diǎn)隔離到每個線程中,從而消除了大并發(fā)情況下競爭問題,提升了性能。

并發(fā)競爭的整體優(yōu)化思路:lock -> cas + volatile -> free lock

到此這篇關(guān)于Java中的Random和ThreadLocalRandom詳細(xì)解析的文章就介紹到這了,更多相關(guān)Random和ThreadLocalRandom解析內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論