日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

【Java】深入理解Java随机数

發布時間:2025/3/15 java 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Java】深入理解Java随机数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

隨機數

根據密碼學原理,隨機數的隨機性檢驗可以分為三個標準:

  • 統計學偽隨機性。統計學偽隨機性指的是在給定的隨機比特流樣本中,1的數量大致等于0的數量,同理,“10”“01”“00”“11”四者數量大致相等。類似的標準被稱為統計學隨機性。滿足這類要求的數字在人類“一眼看上去”是隨機的。
  • 密碼學安全偽隨機性。其定義為,給定隨機樣本的一部分和隨機算法,不能有效的演算出隨機樣本的剩余部分。
  • 真隨機性。其定義為隨機樣本不可重現。實際上只要給定邊界條件,真隨機數并不存在,可是如果產生一個真隨機數樣本的邊界條件十分復雜且難以捕捉(比如計算機當地的本底輻射波動值),可以認為用這個方法演算出來了真隨機數。

相應的,隨機數也分為三類:

  • 偽隨機數:滿足第一個條件的隨機數。
  • 密碼學安全的偽隨機數:同時滿足前兩個條件的隨機數。可以通過密碼學安全偽隨機數生成器計算得出。
  • 真隨機數:同時滿足三個條件的隨機數。

java.lang.Math.random()

定義:public static double random()

源碼實現:

public static double random() {return Math.RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble(); } private static final class RandomNumberGeneratorHolder {static final Random randomNumberGenerator = new Random();private RandomNumberGeneratorHolder() {}}

根據源碼分析得:

java.lang.Math 類里有一個私有靜態內部類,內有一個靜態的 java.util.Random 類對象,調用其 nextDouble() 方法,生成 [0.0, 1.0) 范圍內的偽隨機浮點數。

注意:使用的時候別忘了強轉int或者long,除非需要的是浮點數。

java.util.Random

主要API:

  • protected int next?(int bits):生成下一個偽隨機數。(注意protected,直接調用不了的)
  • public boolean nextBoolean():從此隨機數生成器的序列中返回下一個偽隨機、均勻分布的布爾值。
  • public void nextBytes?(byte[] bytes):生成隨機字節并將其放入用戶提供的字節數組中。
  • public double nextDouble():返回下一個偽隨機數,它是此隨機數生成器的序列中介于0.0和1.0之間的均勻分布的double值。
  • public float nextFloat():返回下一個偽隨機數,此隨機數生成器的序列在0.0和1.0之間均勻分布的float值。
  • public double nextGaussian():返回下一個偽隨機數,與該隨機數生成器的序列的 μ=0.0μ=0.0μ=0.0σ2=1.0σ^2=1.0σ2=1.0 的高斯(“正態”)分布雙精度值。
  • public int nextInt():返回下一個偽隨機數,它是此隨機數生成器序列中均勻分布的int值。
  • public int nextInt?(int bound):返回一個偽隨機數,它從此隨機數生成器的序列中提取,在0()和指定值(不含)之間均勻分布的int值。
  • public long nextLong():返回下一個偽隨機數,該隨機數是從此隨機數生成器的序列中均勻分布的long值。

通過new Random().nextInt(to-from)+from,輸出兩端的邊界,就可以生成左閉右開區間的隨機整數了。

還可以自己借助循環逐位生成的大隨機數:

Random random = new Random(); StringBuilder result = new StringBuilder(); for (int i = 0; i < 9; i++) {result.append(random.nextInt(10)); } System.out.println(Long.parseLong(result.toString()));

Long.parseLong(result.toString())這步處理是為了消除先導0。

或者一步到位:

Random random = new Random(); StringBuilder result = new StringBuilder(); result.append(random.nextInt(9)+1); for (int i = 0; i < 8; i++) {result.append(random.nextInt(10)); } System.out.println(Long.parseLong(result.toString()));

對于 java.util.Random,JVM 通過傳入的種子(seed)來確定生成隨機數的區間。

種子是一個數字,可稱“種子值”,它為生成新的隨機數提供了基礎。
只要種子值相同,獲取的隨機數的序列就是一致的,而且生成的結果都是可以預測的。

這里seed對象的類型是 java.util.concurrent.AtomicLong,而不是簡簡單單的long。

種子值還是可變的。

public void setSeed?(long seed) 方法可生成隨機數:

public synchronized void setSeed(long seed) {this.seed.set(initialScramble(seed));this.haveNextNextGaussian = false; }

調用的initialScramble():

private static long initialScramble(long seed) {return (seed ^ 25214903917L) & 281474976710655L; }

這是一種偽隨機數的實現,而不是真正的隨機數 。

偽隨機只是統計學上的概念,生成的偽隨機數是有一定規律的,而這個規律出現的周期隨著偽隨機算法的優劣而不同。一般來說這個“周期”比較長,但是也是可以預測的。

再看看next(int bits)方法的源碼:

protected int next(int bits) {AtomicLong seed = this.seed;long oldseed;long nextseed;do {oldseed = seed.get();nextseed = oldseed * 25214903917L + 11L & 281474976710655L;} while(!seed.compareAndSet(oldseed, nextseed));return (int)(nextseed >>> 48 - bits); }

Random 類是線程安全的,根據 next(int bits) 還有seed的屬性,可以看出其內部使用 CAS 來保證線程安全性。

一般而言,CAS 相比加鎖有一定的優勢(參考“樂觀鎖”),但并不一定意味著高效
我們可以在每次使用 Random 時都去 new 一個新的線程私有化的 Random 對象。在不同線程上并發使用相同的Random實例可能會導致爭用,從而導致性能不佳,問題源于使用種子來生成隨機數。

首先,舊種子和新種子存儲在兩個輔助變量上。在這一點上,創造新種子的規則并不重要。

要保存新種子,使用 compareAndSet() 方法將舊種子替換為下一個新種子,但這僅僅在舊種子對應于當前設置的種子的條件下才會觸發。

如果此時的值由并發線程操縱,則該方法返回false,這意味著舊值與例外值不匹配。因為是循環內進行的操作,那么會發生自旋,直到變量與例外值匹配。這可能會導致性能不佳和線程競爭。

使用 java.lang.ThreadLocal 來維護線程私有化對象 以及 使用java.util.concurrent.ThreadLocalRandom 都更適合于多線程并發環境。

java.util.concurrent.ThreadLocalRandom

ThreadLocalRandom是隔離到當前線程的隨機數生成器。

像Math類使用的全局Random生成器一樣,ThreadLocalRandom會使用內部生成的種子進行初始化,否則無法進行修改。

ThreadLocalRandom 繼承了Random并添加選項以限制其使用到相應的線程實例。為此,ThreadLocalRandom的實例保存在相應線程的內部映射中,并通過調用current()來返回對應的Random。

如果適用的話,在并發程序中使用ThreadLocalRandom而不是共享Random對象通常會遇到更少的開銷和競爭。

當多個任務(例如,每個ForkJoinTask)在線程池中并行使用隨機數時,使用ThreadLocalRandom特別合適。

ThreadLocalRandom的構造器是private的:

private ThreadLocalRandom() {}

獲取對象要調用current():

public static ThreadLocalRandom current() {if (U.getInt(Thread.currentThread(), PROBE) == 0) {localInit();}return instance; }

當所有用法都是這種形式時,永遠不可能在多個線程之間意外地共享ThreadLocalRandom。

調用方法:

ThreadLocalRandom.current().nextX()

nextX()包括nextBoolean()、nextDouble()、nextFloat()、nextInt()、nextLong()等……

例如:

ThreadLocalRandom.current().nextInt()

ThreadLocalRandom的實例不是加密安全的,還是普通的“偽隨機數”。考慮在對安全敏感的應用程序中使用SecureRandom。此外,除非系統屬性java.util.secureRandomSeed設置為true,否則默認構造的實例不會使用加密的隨機種子。

java.security.SecureRandom

通過對Random的一些分析我們可以知道Random事實上是偽隨機,是可以推導出規律的,而且依賴種子(seed)。如果抽獎或者其他一些對隨機數敏感的場景時,用Random不合適。JDK提供了 java.security.SecureRandom 來解決問題。

SecureRandom提供了加密功能強的隨機數生成器(RNG)。

加密強度高的隨機數至少要符合FIPS 140-2“加密模塊的安全性要求”第4.9.1節中指定的統計隨機數生成器測試。此外,SecureRandom必須產生不確定的輸出。因此,傳遞給SecureRandom對象的任何種子材料都必須不可預測,并且所有SecureRandom輸出序列必須具有加密強度,如RFC 4086:安全性的隨機性要求中所述。

許多SecureRandom實現采用偽隨機數生成器(PRNG,也稱為確定性隨機位生成器或DRBG)的形式,這意味著它們使用確定性算法從隨機種子生成偽隨機序列。其他實現可以產生真正的隨機數,而其他實現則可以使用兩種技術的組合。

SecureRandom是強隨機數生成器,它可以產生高強度的隨機數,產生高強度的隨機數依賴兩個重要的因素:種子算法。算法是可以有很多的,通常如何選擇種子是非常關鍵的因素。
Random的種子是 System.currentTimeMillis(),所以它的隨機數都是可預測的, 是弱偽隨機數。

強偽隨機數的生成思路:收集計算機的各種信息,鍵盤輸入時間,內存使用狀態,硬盤空閑空間,IO延時,進程數量,線程數量等信息,CPU時鐘,來得到一個近似隨機的種子,主要是達到不可預測性

說的更通俗就是,使用加密算法生成很長的一個隨機種子,讓人無法猜測出種子,也就無法推導出隨機序列數。

調用者通過無參數構造函數或getInstance方法之一獲取SecureRandom實例。

例如:

  • SecureRandom r1 = new SecureRandom();
  • SecureRandom r2 = SecureRandom.getInstance("NativePRNG");
  • SecureRandom r3 = SecureRandom.getInstance("DRBG", DrbgParameters.instantiation(128, RESEED_ONLY, null));

上面的第三條語句返回支持特定實例化參數的特定算法的SecureRandom對象。實現的有效實例化參數必須匹配此最小請求,但不一定相同。例如,即使請求不需要某個功能,實際的實例也可以提供該功能。一個實現可以延遲地實例化SecureRandom,直到它被實際使用為止,但是有效的實例化參數必須在創建后立即確定,并且getParameters() 始終應返回不變的相同結果。

SecureRandom的典型調用者調用以下方法來檢索隨機字節:

  • SecureRandom random = new SecureRandom();
  • byte[] bytes = new byte[20];
  • random.nextBytes(bytes);

調用者還可以調用generateSeed(int)方法來生成給定數量的種子字節(例如,為其他隨機數生成器提供種子):byte[] seed = random.generateSeed(20);

不播種新創建的PRNG SecureRandom對象(除非它是由SecureRandom(byte [])創建的)。對nextBytes的首次調用將強制其從實現特定的熵源中播種自身。如果先前調用過setSeed,則不會發生這種自我播種。

通過調用reseed或setSeed方法,可以隨時重新播種SecureRandom。重新設定種子的方法從其熵源讀取熵輸入以重新設定其自身的種子。 setSeed方法要求調用者提供種子。

請注意,并非所有SecureRandom實施都支持種子

一些SecureRandom實現可能在其nextBytes(byte [],SecureRandomParameters)和reseed(SecureRandomParameters)方法中接受SecureRandomParameters參數,以進一步控制這些方法的行為。

注意:根據實現的不同,例如,在各種類Unix操作系統上,如果熵源是/dev/random,則在收集熵時,generateSeed、reseed、nextBytes方法可能會阻塞。

SecureRandom對象可安全用于多個并發線程。

通過在注冊提供程序時將服務提供程序屬性“ ThreadSafe”設置為“ true”,SecureRandom服務提供程序可以公告它是線程安全的。 否則,此類將改為同步對SecureRandomSpi實現的以下方法的訪問:

  • SecureRandomSpi.engineSetSeed(byte[])
  • SecureRandomSpi.engineNextBytes(byte[])
  • SecureRandomSpi.engineNextBytes(byte[], SecureRandomParameters)
  • SecureRandomSpi.engineGenerateSeed(int)
  • SecureRandomSpi.engineReseed(SecureRandomParameters)

System.currentTimeMillis()

可以用System.currentTimeMillis()生成隨機數:

利用System.currentTimeMillis(),獲取從1970年1月1日0時0分0秒(這與UNIX系統有關,Java就這么搞的)到此刻的一個long型的毫秒數,取模之后即可得到所需范圍內的隨機數。

int max=100,min=1; long randomNum = System.currentTimeMillis(); int ran3 = (int) (randomNum%(max-min)+min); System.out.println(ran3);

總結

以上是生活随笔為你收集整理的【Java】深入理解Java随机数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。