自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java隨機(jī)數(shù)的陷阱

開發(fā) 后端
隨機(jī)數(shù)我們應(yīng)該不陌生,業(yè)務(wù)中我們用它來生成驗(yàn)證碼,或者對重復(fù)性要求不高的id,甚至我們還用它在年會(huì)上搞抽獎(jiǎng)。今天我們來探討一下這個(gè)東西。如果使用不當(dāng)會(huì)引發(fā)一系列問題。

前言

隨機(jī)數(shù)我們應(yīng)該不陌生,業(yè)務(wù)中我們用它來生成驗(yàn)證碼,或者對重復(fù)性要求不高的id,甚至我們還用它在年會(huì)上搞抽獎(jiǎng)。今天我們來探討一下這個(gè)東西。如果使用不當(dāng)會(huì)引發(fā)一系列問題。

[[276557]]

Java中的隨機(jī)數(shù)

我們需要在Java中隨機(jī)生成一個(gè)數(shù)字。java開發(fā)中我們通常使用java.util.Random來搞,它提供了一種偽隨機(jī)的生成機(jī)制。Jvm 通過傳入的種子(seed)來確定生成隨機(jī)數(shù)的區(qū)間,只要種子一樣,獲取的隨機(jī)數(shù)的序列就是一致的。而且生成的結(jié)果都是可以預(yù)測的。是一種偽隨機(jī)數(shù)的實(shí)現(xiàn),而不是真正的隨機(jī)數(shù)。來確定使用的但是有些用例直接使用可能會(huì)導(dǎo)致一些意想不到的問題。Random的一個(gè)普遍用法:

  1. // Random 實(shí)例 
  2. Random random = new Random(); 
  3. //調(diào)用 nextInt() 方法 此外還有nextDouble(), nextBoolean(), nextFloat(), ... 
  4. random.nextInt(); 

或者,我們可以使用java中的數(shù)學(xué)計(jì)算類:

  1. Math.random(); 

Math類只包含一個(gè)Random實(shí)例來生成隨機(jī)數(shù):

  1. public static double random() { 
  2.  Random rnd = randomNumberGenerator; 
  3.  if (rnd == null) { 
  4.  // 返回一個(gè)新的Random實(shí)例 
  5.  rnd = initRNG(); 
  6.  } 
  7.  return rnd.nextDouble(); 
  8.  } 

java.util.Random的用法是線程安全的。但是,在不同線程上并發(fā)使用相同的Random實(shí)例可能會(huì)導(dǎo)致爭用,從而導(dǎo)致性能不佳。其原因是使用所謂的種子來生成隨機(jī)數(shù)。種子是一個(gè)簡單的數(shù)字,它為生成新的隨機(jī)數(shù)提供了基礎(chǔ)。我們來看看Random中的next(int bits)方法:

  1. protected int next(int bits) { 
  2.  long oldseed, nextseed; 
  3.  AtomicLong seed = this.seed; 
  4.  do { 
  5.  oldseed = seed.get(); 
  6.  nextseed = (oldseed * multiplier addend) & mask; 
  7.  } while (!seed.compareAndSet(oldseed, nextseed)); 
  8.  return (int)(nextseed >>> (48 - bits));} 

首先,舊種子和新種子存儲在兩個(gè)輔助變量上。在這一點(diǎn)上,創(chuàng)造新種子的原則并不重要。要保存新種子,使用compareAndSet()方法將舊種子替換為下一個(gè)新種子,但這僅僅在舊種子對應(yīng)于當(dāng)前設(shè)置的種子的條件下才會(huì)觸發(fā)。如果此時(shí)的值由并發(fā)線程操縱,則該方法返回false,這意味著舊值與例外值不匹配。因?yàn)槭茄h(huán)內(nèi)進(jìn)行的操作,那么會(huì)發(fā)生自旋,直到變量與例外值匹配。這可能會(huì)導(dǎo)致性能不佳和線程競爭。

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

如果更多線程主動(dòng)生成具有相同Random的實(shí)例的新隨機(jī)數(shù),則上述情況發(fā)生的概率越高。對于生成許多(非常多)隨機(jī)數(shù)的程序,不建議使用這種方式。在這種情況下,您應(yīng)該使用ThreadLocalRandom,它在1.7版本中添加到Java中。ThreadLocalRandom擴(kuò)展了Random并添加選項(xiàng)以限制其使用到相應(yīng)的線程實(shí)例。為此,ThreadLocalRandom的實(shí)例保存在相應(yīng)線程的內(nèi)部映射中,并通過調(diào)用current()來返回對應(yīng)的Random。使用方式如下:

  1. ThreadLocalRandom.current().nextInt() 

安全的隨機(jī)數(shù)

通過對Random的一些分析我們可以知道Random事實(shí)上是偽隨機(jī),是可以推導(dǎo)出規(guī)律的,而且依賴種子(seed)。如果我們搞抽獎(jiǎng)或者其他一些對隨機(jī)數(shù)敏感的場景時(shí),用Random就不合適了,容易被人鉆空子。JDK提供了SecureRandom來解決這個(gè)事情。

SecureRandom是強(qiáng)隨機(jī)數(shù)生成器,它可以產(chǎn)生高強(qiáng)度的隨機(jī)數(shù),產(chǎn)生高強(qiáng)度的隨機(jī)數(shù)依賴兩個(gè)重要的因素:種子和算法。算法是可以有很多的,通常如何選擇種子是非常關(guān)鍵的因素。 Random的種子是System.currentTimeMillis(),所以它的隨機(jī)數(shù)都是可預(yù)測的, 是弱偽隨機(jī)數(shù)。強(qiáng)偽隨機(jī)數(shù)的生成思路:收集計(jì)算機(jī)的各種信息,鍵盤輸入時(shí)間,內(nèi)存使用狀態(tài),硬盤空閑空間,IO延時(shí),進(jìn)程數(shù)量,線程數(shù)量等信息,CPU時(shí)鐘,來得到一個(gè)近似隨機(jī)的種子,主要是達(dá)到不可預(yù)測性。說的更通俗就是,使用加密算法生成很長的一個(gè)隨機(jī)種子,讓你無法猜測出種子,也就無法推導(dǎo)出隨機(jī)序列數(shù)。

總結(jié)

今天我們探討了業(yè)務(wù)中經(jīng)常使用的隨機(jī)數(shù)的一些機(jī)制和一些場景下的一些陷阱,希望你在使用隨機(jī)數(shù)的時(shí)候能避免這種陷阱。

 

責(zé)任編輯:華軒 來源: segmentfault
相關(guān)推薦

2012-03-22 09:31:14

Java

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

2021-12-27 09:31:20

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

2024-11-01 15:51:06

2025-01-17 00:00:00

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

2023-01-03 07:49:45

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

2021-06-01 22:31:57

區(qū)塊鏈隨機(jī)數(shù)技術(shù)

2024-01-25 11:32:21

2010-10-09 15:35:25

MySQL rand函

2010-03-22 19:41:31

2017-05-29 09:56:25

2014-05-13 10:21:00

2009-06-17 17:37:43

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

2009-12-08 11:44:14

PHP獲取隨機(jī)數(shù)

2010-09-06 17:40:59

SQL函數(shù)

2010-03-23 09:47:38

Python隨機(jī)數(shù)Python隨機(jī)字符串

2009-12-08 12:58:33

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

2010-07-15 13:34:32

Perl隨機(jī)數(shù)
點(diǎn)贊
收藏

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