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

Java中生成隨機(jī)數(shù)的4種方式與區(qū)別詳解

 更新時(shí)間:2021年06月16日 10:18:31   作者:Java中文社群  
生成隨機(jī)數(shù)是我們?nèi)粘i_(kāi)發(fā)經(jīng)常會(huì)遇到的一個(gè)功能,這篇文章主要給大家介紹了關(guān)于Java中生成隨機(jī)數(shù)的4種方式與區(qū)別、應(yīng)用場(chǎng)景的相關(guān)資料,4個(gè)方式分別是Random、ThreadLocalRandom、SecureRandom以及Math,需要的朋友可以參考下

在 Java 中,生成隨機(jī)數(shù)的場(chǎng)景有很多,所以本文我們就來(lái)盤(pán)點(diǎn)一下 4 種生成隨機(jī)數(shù)的方式,以及它們之間的區(qū)別和每種生成方式所對(duì)應(yīng)的場(chǎng)景。

1.Random

Random 類(lèi)誕生于 JDK 1.0,它產(chǎn)生的隨機(jī)數(shù)是偽隨機(jī)數(shù),也就是有規(guī)則的隨機(jī)數(shù)。Random 使用的隨機(jī)算法為 linear congruential pseudorandom number generator (LGC) 線(xiàn)性同余法偽隨機(jī)數(shù)。在隨機(jī)數(shù)生成時(shí),隨機(jī)算法的起源數(shù)字稱(chēng)為種子數(shù)(seed),在種子數(shù)的基礎(chǔ)上進(jìn)行一定的變換,從而產(chǎn)生需要的隨機(jī)數(shù)字。

Random 對(duì)象在種子數(shù)相同的情況下,相同次數(shù)生成的隨機(jī)數(shù)是相同的。比如兩個(gè)種子數(shù)相同的 Random 對(duì)象,第一次生成的隨機(jī)數(shù)字完全相同,第二次生成的隨機(jī)數(shù)字也完全相同。默認(rèn)情況下 new Random() 使用的是當(dāng)前納秒時(shí)間作為種子數(shù)的。

① 基礎(chǔ)使用

使用 Random 生成一個(gè)從 0 到 10 的隨機(jī)數(shù)(不包含 10),實(shí)現(xiàn)代碼如下:

// 生成 Random 對(duì)象
Random random = new Random();
for (int i = 0; i < 10; i++) {
    // 生成 0-9 隨機(jī)整數(shù)
    int number = random.nextInt(10);
    System.out.println("生成隨機(jī)數(shù):" + number);
}

以上程序的執(zhí)行結(jié)果為:

② 優(yōu)缺點(diǎn)分析

Random 使用 LGC 算法生成偽隨機(jī)數(shù)的優(yōu)點(diǎn)是執(zhí)行效率比較高,生成的速度比較快。

它的缺點(diǎn)是如果 Random 的隨機(jī)種子一樣的話(huà),每次生成的隨機(jī)數(shù)都是可預(yù)測(cè)的(都是一樣的)。如下代碼所示,當(dāng)我們給兩個(gè)線(xiàn)程設(shè)置相同的種子數(shù)的時(shí)候,會(huì)發(fā)現(xiàn)每次產(chǎn)生的隨機(jī)數(shù)也是相同的:

 // 創(chuàng)建兩個(gè)線(xiàn)程
for (int i = 0; i < 2; i++) {
    new Thread(() -> {
        // 創(chuàng)建 Random 對(duì)象,設(shè)置相同的種子
        Random random = new Random(1024);
        // 生成 3 次隨機(jī)數(shù)
        for (int j = 0; j < 3; j++) {
            // 生成隨機(jī)數(shù)
            int number = random.nextInt();
            // 打印生成的隨機(jī)數(shù)
            System.out.println(Thread.currentThread().getName() + ":" +
                               number);
            // 休眠 200 ms
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("---------------------");
        }
    }).start();
}

以上程序的執(zhí)行結(jié)果為:

③ 線(xiàn)程安全問(wèn)題

當(dāng)我們要使用一個(gè)類(lèi)時(shí),我們首先關(guān)心的第一個(gè)問(wèn)題是:它是否為線(xiàn)程安全?對(duì)于 Random 來(lái)說(shuō),Random 是線(xiàn)程安全的。

PS:線(xiàn)程安全指的是在多線(xiàn)程的場(chǎng)景下,程序的執(zhí)行結(jié)果和預(yù)期的結(jié)果一致,就叫線(xiàn)程安全的,否則則為非線(xiàn)程安全的(也叫線(xiàn)程安全問(wèn)題)。比如有兩個(gè)線(xiàn)程,第一個(gè)線(xiàn)程執(zhí)行 10 萬(wàn)次 ++ 操作,第二個(gè)線(xiàn)程執(zhí)行 10 萬(wàn)次 -- 操作,那么最終的結(jié)果應(yīng)該是沒(méi)加也沒(méi)減,如果程序最終的結(jié)果和預(yù)期不符,則為非線(xiàn)程安全的。

我們來(lái)看 Random 的實(shí)現(xiàn)源碼:

public Random() {
    this(seedUniquifier() ^ System.nanoTime());
}

public int nextInt() {
    return next(32);
}

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)); // CAS(Compare and Swap)生成隨機(jī)數(shù)
    return (int)(nextseed >>> (48 - bits));
}

PS:本文所有源碼來(lái)自于 JDK 1.8.0_211。

從以上源碼可以看出,Random 底層使用的是 CAS(Compare and Swap,比較并替換)來(lái)解決線(xiàn)程安全問(wèn)題的,因此對(duì)于絕大數(shù)隨機(jī)數(shù)生成的場(chǎng)景,使用 Random 不乏為一種很好的選擇。

PS:Java 并發(fā)機(jī)制實(shí)現(xiàn)原子操作有兩種:一種是鎖,一種是 CAS。

CAS 是 Compare And Swap(比較并替換)的縮寫(xiě),java.util.concurrent.atomic 中的很多類(lèi),如(AtomicInteger AtomicBoolean AtomicLong等)都使用了 CAS 機(jī)制來(lái)實(shí)現(xiàn)。

2.ThreadLocalRandom

ThreadLocalRandom 是 JDK 1.7 新提供的類(lèi),它屬于 JUC(java.util.concurrent)下的一員,為什么有了 Random 之后還會(huì)再創(chuàng)建一個(gè) ThreadLocalRandom?

原因很簡(jiǎn)單,通過(guò)上面 Random 的源碼我們可以看出,Random 在生成隨機(jī)數(shù)時(shí)使用的 CAS 來(lái)解決線(xiàn)程安全問(wèn)題的,然而** CAS 在線(xiàn)程競(jìng)爭(zhēng)比較激烈的場(chǎng)景中效率是非常低的**,原因是 CAS 對(duì)比時(shí)老有其他的線(xiàn)程在修改原來(lái)的值,所以導(dǎo)致 CAS 對(duì)比失敗,所以它要一直循環(huán)來(lái)嘗試進(jìn)行 CAS 操作。所以在多線(xiàn)程競(jìng)爭(zhēng)比較激烈的場(chǎng)景可以使用 ThreadLocalRandom 來(lái)解決 Random 執(zhí)行效率比較低的問(wèn)題。

當(dāng)我們第一眼看到 ThreadLocalRandom 的時(shí)候,一定會(huì)聯(lián)想到一次類(lèi) ThreadLocal,確實(shí)如此。ThreadLocalRandom 的實(shí)現(xiàn)原理與 ThreadLocal 類(lèi)似,它相當(dāng)于給每個(gè)線(xiàn)程一個(gè)自己的本地種子,從而就可以避免因多個(gè)線(xiàn)程競(jìng)爭(zhēng)一個(gè)種子,而帶來(lái)的額外性能開(kāi)銷(xiāo)了。

① 基礎(chǔ)使用

接下來(lái)我們使用 ThreadLocalRandom 來(lái)生成一個(gè) 0 到 10 的隨機(jī)數(shù)(不包含 10),實(shí)現(xiàn)代碼如下:

// 得到 ThreadLocalRandom 對(duì)象
ThreadLocalRandom random = ThreadLocalRandom.current();
for (int i = 0; i < 10; i++) {
    // 生成 0-9 隨機(jī)整數(shù)
    int number = random.nextInt(10);
    // 打印結(jié)果
    System.out.println("生成隨機(jī)數(shù):" + number);
}

以上程序的執(zhí)行結(jié)果為:

② 實(shí)現(xiàn)原理

ThreadLocalRandom 的實(shí)現(xiàn)原理和 ThreadLocal 類(lèi)似,它是讓每個(gè)線(xiàn)程持有自己的本地種子,該種子在生成隨機(jī)數(shù)時(shí)候才會(huì)被初始化,實(shí)現(xiàn)源碼如下:

public int nextInt(int bound) {
    // 參數(shù)效驗(yàn)
    if (bound <= 0)
        throw new IllegalArgumentException(BadBound);
    // 根據(jù)當(dāng)前線(xiàn)程中種子計(jì)算新種子
    int r = mix32(nextSeed());
    int m = bound - 1;
    // 根據(jù)新種子和 bound 計(jì)算隨機(jī)數(shù)
    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;
}

final long nextSeed() {
    Thread t; long r; // read and update per-thread seed
    // 獲取當(dāng)前線(xiàn)程中 threadLocalRandomSeed 變量,然后在種子的基礎(chǔ)上累加 GAMMA 值作為新種子
    // 再使用 UNSAFE.putLong 將新種子存放到當(dāng)前線(xiàn)程的 threadLocalRandomSeed 變量中
    UNSAFE.putLong(t = Thread.currentThread(), SEED,
                   r = UNSAFE.getLong(t, SEED) + GAMMA); 
    return r;
}

③ 優(yōu)缺點(diǎn)分析

ThreadLocalRandom 結(jié)合了 Random 和 ThreadLocal 類(lèi),并被隔離在當(dāng)前線(xiàn)程中。因此它通過(guò)避免競(jìng)爭(zhēng)操作種子數(shù),從而在多線(xiàn)程運(yùn)行的環(huán)境中實(shí)現(xiàn)了更好的性能,而且也保證了它的線(xiàn)程安全。

另外,不同于 Random, ThreadLocalRandom 明確不支持設(shè)置隨機(jī)種子。它重寫(xiě)了 Random 的
setSeed(long seed) 方法并直接拋出了 UnsupportedOperationException 異常,因此降低了多個(gè)線(xiàn)程出現(xiàn)隨機(jī)數(shù)重復(fù)的可能性。

源碼如下:

public void setSeed(long seed) {
    // only allow call from super() constructor
    if (initialized)
        throw new UnsupportedOperationException();
}

只要程序中調(diào)用了 setSeed() 方法就會(huì)拋出 UnsupportedOperationException 異常,如下圖所示:

ThreadLocalRandom 缺點(diǎn)分析

雖然 ThreadLocalRandom 不支持手動(dòng)設(shè)置隨機(jī)種子的方法,但并不代表 ThreadLocalRandom 就是完美的,當(dāng)我們查看 ThreadLocalRandom 初始化隨機(jī)種子的方法 initialSeed() 源碼時(shí)發(fā)現(xiàn),默認(rèn)情況下它的隨機(jī)種子也是以當(dāng)前時(shí)間有關(guān),源碼如下:

private static long initialSeed() {
    // 嘗試獲取 JVM 的啟動(dòng)參數(shù)
    String sec = VM.getSavedProperty("java.util.secureRandomSeed");
    // 如果啟動(dòng)參數(shù)設(shè)置的值為 true,則參數(shù)一個(gè)隨機(jī) 8 位的種子
    if (Boolean.parseBoolean(sec)) {
        byte[] seedBytes = java.security.SecureRandom.getSeed(8);
        long s = (long)(seedBytes[0]) & 0xffL;
        for (int i = 1; i < 8; ++i)
            s = (s << 8) | ((long)(seedBytes[i]) & 0xffL);
        return s;
    }
    // 如果沒(méi)有設(shè)置啟動(dòng)參數(shù),則使用當(dāng)前時(shí)間有關(guān)的隨機(jī)種子算法
    return (mix64(System.currentTimeMillis()) ^
            mix64(System.nanoTime()));
}

從上述源碼可以看出,當(dāng)我們?cè)O(shè)置了啟動(dòng)參數(shù)“-Djava.util.secureRandomSeed=true”時(shí),ThreadLocalRandom 會(huì)產(chǎn)生一個(gè)隨機(jī)種子,一定程度上能緩解隨機(jī)種子相同所帶來(lái)隨機(jī)數(shù)可預(yù)測(cè)的問(wèn)題,然而默認(rèn)情況下如果不設(shè)置此參數(shù),那么在多線(xiàn)程中就可以因?yàn)閱?dòng)時(shí)間相同,而導(dǎo)致多個(gè)線(xiàn)程在每一步操作中都會(huì)生成相同的隨機(jī)數(shù)。

3.SecureRandom

SecureRandom 繼承自 Random,該類(lèi)提供加密強(qiáng)隨機(jī)數(shù)生成器。SecureRandom 不同于 Random,它收集了一些隨機(jī)事件,比如鼠標(biāo)點(diǎn)擊,鍵盤(pán)點(diǎn)擊等,SecureRandom 使用這些隨機(jī)事件作為種子。這意味著,種子是不可預(yù)測(cè)的,而不像 Random 默認(rèn)使用系統(tǒng)當(dāng)前時(shí)間的毫秒數(shù)作為種子,從而避免了生成相同隨機(jī)數(shù)的可能性。

基礎(chǔ)使用

// 創(chuàng)建 SecureRandom 對(duì)象,并設(shè)置加密算法
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
for (int i = 0; i < 10; i++) {
    // 生成 0-9 隨機(jī)整數(shù)
    int number = random.nextInt(10);
    // 打印結(jié)果
    System.out.println("生成隨機(jī)數(shù):" + number);
}

以上程序的執(zhí)行結(jié)果為:

SecureRandom 默認(rèn)支持兩種加密算法:

  1. SHA1PRNG 算法,提供者 sun.security.provider.SecureRandom;
  2. NativePRNG 算法,提供者 sun.security.provider.NativePRNG。

當(dāng)然除了上述的操作方式之外,你還可以選擇使用 new SecureRandom() 來(lái)創(chuàng)建 SecureRandom 對(duì)象,實(shí)現(xiàn)代碼如下:

SecureRandom secureRandom = new SecureRandom();

通過(guò) new 初始化 SecureRandom,默認(rèn)會(huì)使用 NativePRNG 算法來(lái)生成隨機(jī)數(shù),但是也可以配置 JVM 啟動(dòng)參數(shù)“-Djava.security”參數(shù)來(lái)修改生成隨機(jī)數(shù)的算法,或選擇使用 getInstance("算法名稱(chēng)") 的方式來(lái)指定生成隨機(jī)數(shù)的算法。

4.Math

Math 類(lèi)誕生于 JDK 1.0,它里面包含了用于執(zhí)行基本數(shù)學(xué)運(yùn)算的屬性和方法,如初等指數(shù)、對(duì)數(shù)、平方根和三角函數(shù),當(dāng)然它里面也包含了生成隨機(jī)數(shù)的靜態(tài)方法 Math.random() ,此方法會(huì)產(chǎn)生一個(gè) 0 到 1 的 double 值,如下代碼所示。

① 基礎(chǔ)使用

for (int i = 0; i < 10; i++) {
    // 產(chǎn)生隨機(jī)數(shù)
    double number = Math.random();
    System.out.println("生成隨機(jī)數(shù):" + number);
}

以上程序的執(zhí)行結(jié)果為:

② 擴(kuò)展

當(dāng)然如果你想用它來(lái)生成一個(gè)一定范圍的 int 值也是可以的,你可以這樣寫(xiě):

for (int i = 0; i < 10; i++) {
    // 生成一個(gè)從 0-99 的整數(shù)
    int number = (int) (Math.random() * 100);
    System.out.println("生成隨機(jī)數(shù):" + number);
}

以上程序的執(zhí)行結(jié)果為:

③ 實(shí)現(xiàn)原理

通過(guò)分析 Math 的源碼我們可以得知:當(dāng)?shù)谝淮握{(diào)用 Math.random() 方法時(shí),自動(dòng)創(chuàng)建了一個(gè)偽隨機(jī)數(shù)生成器,**實(shí)際上用的是 **new java.util.Random(),當(dāng)下一次繼續(xù)調(diào)用 Math.random() 方法時(shí),就會(huì)使用這個(gè)新的偽隨機(jī)數(shù)生成器。

源碼如下:

public static double random() {
    return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}

private static final class RandomNumberGeneratorHolder {
    static final Random randomNumberGenerator = new Random();
}

總結(jié)

本文我們介紹了 4 種生成隨機(jī)數(shù)的方法,其中 Math 是對(duì) Random 的封裝,所以二者比較類(lèi)似。Random 生成的是偽隨機(jī)數(shù),是以當(dāng)前納秒時(shí)間作為種子數(shù)的,并且在多線(xiàn)程競(jìng)爭(zhēng)比較激烈的情況下因?yàn)橐M(jìn)行 CAS 操作,所以存在一定的性能問(wèn)題,但對(duì)于絕大數(shù)應(yīng)用場(chǎng)景來(lái)說(shuō),使用 Random 已經(jīng)足夠了。當(dāng)在競(jìng)爭(zhēng)比較激烈的場(chǎng)景下可以使用 ThreadLocalRandom 來(lái)替代 Random,但如果對(duì)安全性要求比較高的情況下,可以使用 SecureRandom 來(lái)生成隨機(jī)數(shù),因?yàn)?SecureRandom 會(huì)收集一些隨機(jī)事件來(lái)作為隨機(jī)種子,所以 SecureRandom 可以看作是生成真正隨機(jī)數(shù)的一個(gè)工具類(lèi)。

到此這篇關(guān)于Java中生成隨機(jī)數(shù)的4種方式與區(qū)別的文章就介紹到這了,更多相關(guān)Java生成隨機(jī)數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

參考 & 鳴謝

相關(guān)文章

  • idea中Tomcat啟動(dòng)失敗的解決

    idea中Tomcat啟動(dòng)失敗的解決

    這篇文章主要介紹了idea中Tomcat啟動(dòng)失敗的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-09-09
  • Java數(shù)據(jù)結(jié)構(gòu)之二叉搜索樹(shù)詳解

    Java數(shù)據(jù)結(jié)構(gòu)之二叉搜索樹(shù)詳解

    二叉搜索樹(shù)作為一個(gè)經(jīng)典的數(shù)據(jù)結(jié)構(gòu),具有鏈表的快速插入與刪除的特點(diǎn),同時(shí)查詢(xún)效率也很優(yōu)秀,所以應(yīng)用十分廣泛。本文將詳細(xì)講講二叉搜索樹(shù)的原理與實(shí)現(xiàn),需要的可以參考一下
    2022-06-06
  • 詳解Mybatis中萬(wàn)能的Map和模糊查詢(xún)寫(xiě)法

    詳解Mybatis中萬(wàn)能的Map和模糊查詢(xún)寫(xiě)法

    這篇文章主要介紹了Mybatis中萬(wàn)能的Map和模糊查詢(xún)寫(xiě)法的相關(guān)資料,幫助大家更好的理解和使用Mybatis,感興趣的朋友可以了解下
    2021-03-03
  • JAVA如何獲取客戶(hù)端IP地址和MAC地址

    JAVA如何獲取客戶(hù)端IP地址和MAC地址

    本篇文章主要介紹了JAVA如何獲取客戶(hù)端IP地址和MAC地址非常具有實(shí)用價(jià)值,這里整理了詳細(xì)的代碼,需要的朋友可以參考下
    2017-08-08
  • Java并發(fā)編程中的ReentrantLock類(lèi)詳解

    Java并發(fā)編程中的ReentrantLock類(lèi)詳解

    這篇文章主要介紹了Java并發(fā)編程中的ReentrantLock類(lèi)詳解,ReentrantLock是juc.locks包中的一個(gè)獨(dú)占式可重入鎖,相比synchronized,它可以創(chuàng)建多個(gè)條件等待隊(duì)列,還支持公平/非公平鎖、可中斷、超時(shí)、輪詢(xún)等特性,需要的朋友可以參考下
    2023-12-12
  • SpringBoot+logback默認(rèn)日志的配置和使用方式

    SpringBoot+logback默認(rèn)日志的配置和使用方式

    這篇文章主要介紹了SpringBoot+logback默認(rèn)日志的配置和使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • SpringMVC使用自定義驗(yàn)證器進(jìn)行數(shù)據(jù)驗(yàn)證的方法

    SpringMVC使用自定義驗(yàn)證器進(jìn)行數(shù)據(jù)驗(yàn)證的方法

    SpringMVC?提供了強(qiáng)大的數(shù)據(jù)驗(yàn)證機(jī)制,可以方便地驗(yàn)證表單提交的數(shù)據(jù),除了自帶的驗(yàn)證器之外,SpringMVC?還支持自定義驗(yàn)證器,允許開(kāi)發(fā)者根據(jù)業(yè)務(wù)需求自定義驗(yàn)證規(guī)則,本文將介紹如何在?SpringMVC?中使用自定義驗(yàn)證器
    2023-07-07
  • 基于Mybatis Plus實(shí)現(xiàn)代碼生成器CodeGenerator

    基于Mybatis Plus實(shí)現(xiàn)代碼生成器CodeGenerator

    這篇文章主要介紹了基于Mybatis Plus實(shí)現(xiàn)代碼生成器CodeGenerator,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Spring詳細(xì)講解事務(wù)失效的場(chǎng)景

    Spring詳細(xì)講解事務(wù)失效的場(chǎng)景

    實(shí)際項(xiàng)目開(kāi)發(fā)中,如果涉及到多張表操作時(shí),為了保證業(yè)務(wù)數(shù)據(jù)的一致性,大家一般都會(huì)采用事務(wù)機(jī)制,好多小伙伴可能只是簡(jiǎn)單了解一下,遇到事務(wù)失效的情況,便會(huì)無(wú)從下手,下面這篇文章主要給大家介紹了關(guān)于Spring事務(wù)失效場(chǎng)景的相關(guān)資料,需要的朋友可以參考下
    2022-07-07
  • Spring-ImportSelector接口功能使用案例

    Spring-ImportSelector接口功能使用案例

    ImportSelector接口是至spring中導(dǎo)入內(nèi)部類(lèi)或者外部類(lèi)的核心接口,只需要其定義的方法內(nèi)返回需要?jiǎng)?chuàng)建bean的class字符串就好了,這篇文章主要介紹了Spring-ImportSelector接口功能介紹,需要的朋友可以參考下
    2023-09-09

最新評(píng)論