成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java生成隨機(jī)數(shù)的4種方式,以后就用它了!

開(kāi)發(fā) 后端
本文我們介紹 4 種生成隨機(jī)數(shù)的方法,其中 Math 是對(duì) Random 的封裝,所以二者比較類似。Random 生成的是偽隨機(jī)數(shù),是以當(dāng)前納秒時(shí)間作為種子數(shù)的,并且在多線程競(jìng)爭(zhēng)比較激烈的情況下因?yàn)橐M(jìn)行 CAS 操作,所以存在一定的性能問(wèn)題,但對(duì)于絕大數(shù)應(yīng)用場(chǎng)景來(lái)說(shuō),使用 Random 已經(jīng)足夠了。

[[405510]]

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

1.Random

Random 類誕生于 JDK 1.0,它產(chǎn)生的隨機(jī)數(shù)是偽隨機(jī)數(shù),也就是有規(guī)則的隨機(jī)數(shù)。Random 使用的隨機(jī)算法為 linear congruential pseudorandom number generator (LGC) 線性同余法偽隨機(jī)數(shù)。在隨機(jī)數(shù)生成時(shí),隨機(jī)算法的起源數(shù)字稱為種子數(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)代碼如下:

  1. // 生成 Random 對(duì)象 
  2. Random random = new Random(); 
  3. for (int i = 0; i < 10; i++) { 
  4.     // 生成 0-9 隨機(jī)整數(shù) 
  5.     int number = random.nextInt(10); 
  6.     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ī)種子一樣的話,每次生成的隨機(jī)數(shù)都是可預(yù)測(cè)的(都是一樣的)。如下代碼所示,當(dāng)我們給兩個(gè)線程設(shè)置相同的種子數(shù)的時(shí)候,會(huì)發(fā)現(xiàn)每次產(chǎn)生的隨機(jī)數(shù)也是相同的:

  1. // 創(chuàng)建兩個(gè)線程 
  2. or (int i = 0; i < 2; i++) { 
  3.    new Thread(() -> { 
  4.        // 創(chuàng)建 Random 對(duì)象,設(shè)置相同的種子 
  5.        Random random = new Random(1024); 
  6.        // 生成 3 次隨機(jī)數(shù) 
  7.        for (int j = 0; j < 3; j++) { 
  8.            // 生成隨機(jī)數(shù) 
  9.            int number = random.nextInt(); 
  10.            // 打印生成的隨機(jī)數(shù) 
  11.            System.out.println(Thread.currentThread().getName() + ":" + 
  12.                               number); 
  13.            // 休眠 200 ms 
  14.            try { 
  15.                Thread.sleep(200); 
  16.            } catch (InterruptedException e) { 
  17.                e.printStackTrace(); 
  18.            } 
  19.            System.out.println("---------------------"); 
  20.        } 
  21.    }).start(); 

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

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

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

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

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

  1. public Random() { 
  2.     this(seedUniquifier() ^ System.nanoTime()); 
  3.  
  4. public int nextInt() { 
  5.     return next(32); 
  6.  
  7. protected int next(int bits) { 
  8.     long oldseed, nextseed; 
  9.     AtomicLong seed = this.seed; 
  10.     do { 
  11.         oldseed = seed.get(); 
  12.         nextseed = (oldseed * multiplier + addend) & mask; 
  13.     } while (!seed.compareAndSet(oldseed, nextseed)); // CAS(Compare and Swap)生成隨機(jī)數(shù) 
  14.     return (int)(nextseed >>> (48 - bits)); 
  • PS:本文所有源碼來(lái)自于 JDK 1.8.0_211。

從以上源碼可以看出,Random 底層使用的是 CAS(Compare and Swap,比較并替換)來(lái)解決線程安全問(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 中的很多類,如(AtomicInteger AtomicBoolean AtomicLong等)都使用了 CAS 機(jī)制來(lái)實(shí)現(xiàn)。

2.ThreadLocalRandom

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

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

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

① 基礎(chǔ)使用

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

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

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

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

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

  1. public int nextInt(int bound) { 
  2.     // 參數(shù)效驗(yàn) 
  3.     if (bound <= 0) 
  4.         throw new IllegalArgumentException(BadBound); 
  5.     // 根據(jù)當(dāng)前線程中種子計(jì)算新種子 
  6.     int r = mix32(nextSeed()); 
  7.     int m = bound - 1; 
  8.     // 根據(jù)新種子和 bound 計(jì)算隨機(jī)數(shù) 
  9.     if ((bound & m) == 0) // power of two 
  10.         r &= m; 
  11.     else { // reject over-represented candidates 
  12.         for (int u = r >>> 1; 
  13.              u + m - (r = u % bound) < 0; 
  14.              u = mix32(nextSeed()) >>> 1) 
  15.             ; 
  16.     } 
  17.     return r; 
  18.  
  19. final long nextSeed() { 
  20.     Thread t; long r; // read and update per-thread seed 
  21.     // 獲取當(dāng)前線程中 threadLocalRandomSeed 變量,然后在種子的基礎(chǔ)上累加 GAMMA 值作為新種子 
  22.     // 再使用 UNSAFE.putLong 將新種子存放到當(dāng)前線程的 threadLocalRandomSeed 變量中 
  23.     UNSAFE.putLong(t = Thread.currentThread(), SEED, 
  24.                    r = UNSAFE.getLong(t, SEED) + GAMMA);  
  25.     return r; 

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

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

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

源碼如下:

  1. public void setSeed(long seed) { 
  2.     // only allow call from super() constructor 
  3.     if (initialized) 
  4.         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),源碼如下:

  1. private static long initialSeed() { 
  2.     // 嘗試獲取 JVM 的啟動(dòng)參數(shù) 
  3.     String sec = VM.getSavedProperty("java.util.secureRandomSeed"); 
  4.     // 如果啟動(dòng)參數(shù)設(shè)置的值為 true,則參數(shù)一個(gè)隨機(jī) 8 位的種子 
  5.     if (Boolean.parseBoolean(sec)) { 
  6.         byte[] seedBytes = java.security.SecureRandom.getSeed(8); 
  7.         long s = (long)(seedBytes[0]) & 0xffL; 
  8.         for (int i = 1; i < 8; ++i) 
  9.             s = (s << 8) | ((long)(seedBytes[i]) & 0xffL); 
  10.         return s; 
  11.     } 
  12.     // 如果沒(méi)有設(shè)置啟動(dòng)參數(shù),則使用當(dāng)前時(shí)間有關(guān)的隨機(jī)種子算法 
  13.     return (mix64(System.currentTimeMillis()) ^ 
  14.             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ù),那么在多線程中就可以因?yàn)閱?dòng)時(shí)間相同,而導(dǎo)致多個(gè)線程在每一步操作中都會(huì)生成相同的隨機(jī)數(shù)。

3.SecureRandom

SecureRandom 繼承自 Random,該類提供加密強(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ǔ)使用

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

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

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

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

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

  1. 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("算法名稱") 的方式來(lái)指定生成隨機(jī)數(shù)的算法。

4.Math

Math 類誕生于 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ǔ)使用

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

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

② 擴(kuò)展

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

  1. for (int i = 0; i < 10; i++) { 
  2.     // 生成一個(gè)從 0-99 的整數(shù) 
  3.     int number = (int) (Math.random() * 100); 
  4.     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ù)生成器。

源碼如下:

  1. public static double random() { 
  2.     return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble(); 
  3.  
  4. private static final class RandomNumberGeneratorHolder { 
  5.     static final Random randomNumberGenerator = new Random(); 

 總結(jié)

本文我們介紹了 4 種生成隨機(jī)數(shù)的方法,其中 Math 是對(duì) Random 的封裝,所以二者比較類似。Random 生成的是偽隨機(jī)數(shù),是以當(dāng)前納秒時(shí)間作為種子數(shù)的,并且在多線程競(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è)工具類。

 

責(zé)任編輯:姜華 來(lái)源: Java中文社群
相關(guān)推薦

2019-12-26 14:07:19

隨機(jī)數(shù)偽隨機(jī)多線程

2024-11-01 15:51:06

2019-09-11 10:09:00

Java虛擬機(jī)算法

2009-12-02 17:01:01

PHP隨機(jī)數(shù)rand()

2010-03-22 19:41:31

2012-03-22 09:31:14

Java

2017-05-29 09:56:25

2024-05-15 09:09:49

2022-12-15 08:54:28

JAVA性能JDK

2010-03-11 12:48:25

Python生成隨機(jī)數(shù)

2011-07-08 15:11:03

JAVA

2009-06-11 15:16:18

不重復(fù)隨機(jī)數(shù)Java

2015-10-13 10:00:58

Swift隨機(jī)數(shù)使用總結(jié)

2014-07-23 10:07:34

2021-12-27 09:31:20

HashtableJava隨機(jī)數(shù)

2023-01-03 07:49:45

Java隨機(jī)數(shù)線程

2010-10-09 15:35:25

MySQL rand函

2024-01-25 11:32:21

2025-01-17 00:00:00

Java隨機(jī)數(shù)服務(wù)

2021-06-01 22:31:57

區(qū)塊鏈隨機(jī)數(shù)技術(shù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产一区二区观看 | 天天色天天射天天干 | 欧美色欧美亚洲另类七区 | 神马久久久久久久久久 | 国产免费一区二区三区最新6 | 99久久久无码国产精品 | 日韩精品一区二区三区 | 日韩精品一区二区三区在线观看 | 国产美女网站 | 999久久久 | www.成人免费视频 | 亚欧洲精品在线视频免费观看 | 久久久99精品免费观看 | 精品伦精品一区二区三区视频 | 美女久久久久 | 一区二区三区视频在线观看 | 亚洲精品欧美一区二区三区 | 日本超碰| 亚欧洲精品在线视频免费观看 | 亚洲另类春色偷拍在线观看 | 国产成人精品久久二区二区91 | 91久久精品国产91久久性色tv | 四虎成人av | 91婷婷韩国欧美一区二区 | 美女艹b| 亚洲成人国产 | 国产精品一区二区无线 | 日屁视频 | 看av网 | 欧美日韩久久 | 日韩成人高清在线 | 久久久久久a | 国产日韩一区二区三区 | a级毛片免费高清视频 | 国产真实乱全部视频 | 亚洲欧美国产精品久久 | 伊人最新网址 | 男女免费观看在线爽爽爽视频 | 色网站入口 | 毛片国产 | 在线精品一区二区 |