阿里面試官:先問我Math.random()是線程安全的嗎?再問我幾個常見隨機數類的區別?

2021-03-02 Java面試那些事兒

作者:專職跑龍套

來源:https://urlify.cn/vquiEn

# Math.random() 靜態方法


產生的隨機數是 0 - 1 之間的一個 double,即 0<=random<=1。使用:

for (int i = 0; i < 10; i++) {System.out.println(Math.random());}

結果:

0.35986138956064260.26667781453658110.250907310642433550.0110649980616662760.6006862281756390.90840060276294960.127005246548478330.60846058490693430.72908047825142610.9923831908303121

實現原理:

When this method is first called, it creates a single new pseudorandom-number generator, exactly as if by the expression new java.util.Random()This new pseudorandom-number generator is used thereafter for all calls to this method and is used nowhere else.

當第一次調用 Math.random() 方法時,自動創建了一個偽隨機數生成器,實際上用的是 newjava.util.Random()。當接下來繼續調用 Math.random() 方法時,就會使用這個新的偽隨機數生成器。

源碼如下:

public static double random() {Random rnd = randomNumberGenerator;if (rnd == null) rnd = initRNG(); return rnd.nextDouble();}

private static synchronized Random initRNG() {Random rnd = randomNumberGenerator;return (rnd == null) ? (randomNumberGenerator = new Random()) : rnd; }

This method is properly synchronized to allow correct use by more than one thread. However, if many threads need to generate pseudorandom numbers at a great rate, it may reduce contention for each thread to have its own pseudorandom-number generator.

initRNG() 方法是 synchronized 的,因此在多線程情況下,只有一個線程會負責創建偽隨機數生成器(使用當前時間作為種子),其他線程則利用該偽隨機數生成器產生隨機數。

因此 Math.random() 方法是線程安全的。

什麼情況下隨機數的生成線程不安全:

線程1在第一次調用 random() 時產生一個生成器 generator1,使用當前時間作為種子。

線程2在第一次調用 random() 時產生一個生成器 generator2,使用當前時間作為種子。

碰巧 generator1 和 generator2 使用相同的種子,導致 generator1 以後產生的隨機數每次都和 generator2 以後產生的隨機數相同。

什麼情況下隨機數的生成線程安全: Math.random() 靜態方法使用

線程1在第一次調用 random() 時產生一個生成器 generator1,使用當前時間作為種子。

線程2在第一次調用 random() 時發現已經有一個生成器 generator1,則直接使用生成器 generator1。

public class JavaRandom {public static void main(String args[]) {new MyThread().start();new MyThread().start();}}class MyThread extends Thread {public void run() {for (int i = 0; i < 2; i++) {System.out.println(Thread.currentThread().getName() + ": " + Math.random());}}}

結果:

Thread-1: 0.8043581595645333Thread-0: 0.9338269554390357Thread-1: 0.5571569413128877Thread-0: 0.37484586843392464

#  java.util.Random 工具類


基本算法:linear congruential pseudorandom number generator (LGC) 線性同餘法偽隨機數生成器缺點:可預測

An attacker will simply compute the seed from the output values observed. This takes significantly less time than 2^48 in the case of java.util.Random. 從輸出中可以很容易計算出種子值。It is shown that you can predict future Random outputs observing only two(!) output values in time roughly 2^16. 因此可以預測出下一個輸出的隨機數。You should never use an LCG for security-critical purposes.在注重信息安全的應用中,不要使用 LCG 算法生成隨機數,請使用 SecureRandom。

使用:

Random random = new Random();

for (int i = 0; i < 5; i++) {System.out.println(random.nextInt());}

結果:

-24520987-96094681-9526224273002604191489256498

Random類默認使用當前系統時鐘作為種子:

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

public Random(long seed) {if (getClass() == Random.class)this.seed = new AtomicLong(initialScramble(seed));else {this.seed = new AtomicLong(); setSeed(seed);}}

Random類提供的方法:API

nextBoolean() - 返回均勻分布的 true 或者 false

nextBytes(byte[]bytes)

nextDouble() - 返回 0.0 到 1.0 之間的均勻分布的 double

nextFloat() - 返回 0.0 到 1.0 之間的均勻分布的 float

nextGaussian()- 返回 0.0 到 1.0 之間的高斯分布(即正態分布)的 double

nextInt() - 返回均勻分布的 int

nextInt(intn) - 返回 0 到 n 之間的均勻分布的 int (包括 0,不包括 n)

nextLong() - 返回均勻分布的 long

setSeed(longseed) - 設置種子

只要種子一樣,產生的隨機數也一樣: 因為種子確定,隨機數算法也確定,因此輸出是確定的!

Random random1 = new Random(10000);Random random2 = new Random(10000);

for (int i = 0; i < 5; i++) {System.out.println(random1.nextInt() + " = " + random2.nextInt());}

結果:

-498702880 = -498702880-858606152 = -8586061521942818232 = 1942818232-1044940345 = -10449403451588429001 = 1588429001


# java.util.concurrent.ThreadLocalRandom 工具類

ThreadLocalRandom 是 JDK 7 之後提供,也是繼承至 java.util.Random。

private static final ThreadLocal<ThreadLocalRandom> localRandom =new ThreadLocal<ThreadLocalRandom>() {protected ThreadLocalRandom initialValue() {return new ThreadLocalRandom();}};

每一個線程有一個獨立的隨機數生成器,用於並發產生隨機數,能夠解決多個線程發生的競爭爭奪。效率更高! ThreadLocalRandom 不是直接用 new 實例化,而是第一次使用其靜態方法 current() 得到 ThreadLocal<ThreadLocalRandom> 實例,然後調用 java.util.Random 類提供的方法獲得各種隨機數。使用:

public class JavaRandom {public static void main(String args[]) {new MyThread().start();new MyThread().start();}}class MyThread extends Thread {public void run() {for (int i = 0; i < 2; i++) {System.out.println(Thread.currentThread().getName() + ": " + ThreadLocalRandom.current().nextDouble());}}}

結果:

Thread-0: 0.13267085355389086Thread-1: 0.1138484950410098Thread-0: 0.17187774671469858Thread-1: 0.9305225910262372

#  java.Security.SecureRandom


也是繼承至 java.util.Random。

Instances of java.util.Random are not cryptographically secure. Consider instead using SecureRandom to get a cryptographically secure pseudo-random number generator for use by security-sensitive applications.SecureRandom takes Random Data from your os (they can be interval between keystrokes etc - most os collect these data store them in files - /dev/random and /dev/urandom in case of linux/solaris) and uses that as the seed. 

作業系統收集了一些隨機事件,比如滑鼠點擊,鍵盤點擊等等,SecureRandom 使用這些隨機事件作為種子。

SecureRandom 提供加密的強隨機數生成器 (RNG),要求種子必須是不可預知的,產生非確定性輸出。SecureRandom 也提供了與實現無關的算法,因此,調用方(應用程式代碼)會請求特定的 RNG 算法並將它傳回到該算法的 SecureRandom 對象中。

1.如果僅指定算法名稱,如下所示:

SecureRandomrandom=SecureRandom.getInstance("SHA1PRNG");

2.如果既指定了算法名稱又指定了包提供程序,如下所示:

SecureRandomrandom=SecureRandom.getInstance("SHA1PRNG","SUN");

使用:

SecureRandom random1 = SecureRandom.getInstance("SHA1PRNG");SecureRandom random2 = SecureRandom.getInstance("SHA1PRNG");

for (int i = 0; i < 5; i++) {System.out.println(random1.nextInt() + " != " + random2.nextInt());}

結果:

704046703 != 211722993560819811 != 107252259425075610 != -295395347682299589 != -1637998900-1147654329 != 1418666937

#  隨機字符串


可以使用 Apache Commons-Lang 包中的 RandomStringUtils 類。Maven 依賴如下:

<dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency>

API 參考:https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/RandomStringUtils.html

示例:

public class RandomStringDemo {public static void main(String[] args) {String result = RandomStringUtils.random(64, false, true);System.out.println("random = " + result);

result = RandomStringUtils.randomAlphabetic(64);System.out.println("random = " + result);

result = RandomStringUtils.randomAscii(32);System.out.println("random = " + result);

result = RandomStringUtils.random(32, 0, 20, true, true, "qw32rfHIJk9iQ8Ud7h0X".toCharArray());System.out.println("random = " + result);

}}

RandomStringUtils 類的實現上也是依賴了 java.util.Random 工具類:

相關焦點

  • 阿里面試官:來手寫一下Redis的LRU算法 我當場懵了
    小夥子你好,之前問過了你基礎知識以及一些緩存的常見幾個大問題了,那你能跟我聊聊為啥Redis那麼快麼?哦,帥氣迷人的面試官您好,我們可以先看一下關係型資料庫跟Redis本質上的區別。我可以打個比方麼:我記得有過一個小夥伴微信問過我上下文切換是啥,為啥可能會線程不安全,我是這麼說的,就好比你看一本英文書,你看到第十頁發現有個單詞不會讀,你加了個書籤,然後去查字典,過了一會你又回來繼續從書籤那裡讀,ok到目前為止沒啥問題。
  • 阿里面試官問我Java線程和作業系統線程什麼關係
    這個問題是安琪拉之前面試被問到的一個問題,正好順著上一篇文章介紹完線程調用時的用戶態和內核態的切換,後續把Java 並發的都一起講了。面試官:聽前一個面試官說你Java並發這塊掌握的不錯,我們深入的交流一下;我:  看了看面試官頭部稀疏的結締組織,已然覺得這場面試不簡單,不過好在事前把安琪拉的博客看了個遍,有所準備,我回答說:咳咳,掌握的還算可以。
  • 阿里P8面試官:4道Java必考題,答好3題P6穩,全對考慮P7
    面試官內心OS:只掌握到這種程度?那隨便再問兩個問題就讓回去等通知吧。實際面試官想聽到的【更深入的】回答:StringBuffer和StringBuilder都繼承自抽象類AbstractStringBuilder!
  • 面試官說我的簡歷像包裝的,面試後面試官向我道歉了
    下面小編就給大家分享一段面試官與我朋友的面試內容 ▼面試官:你剛才提到的xxx項目,能講一下具體細節以及你負責的模塊嗎(內心os:我要會我能不用嗎  這麼回答總該躲過去了吧)面試官:那你說下Eureka和Zookeeper作為註冊中心的區別。小編朋友:Eureka是AP、zk是CP 。
  • python模塊和包,常用模塊math、random、random.sample(li,n)#不放回抽取n個隨機樣本
    #使用導入後的內容import random #導入random模塊random.randint(1,100) #使用模塊中的randint()生成1-100的隨機數2.from 模塊 import 名字1 [as 別名1],名字2[as 別名2],...
  • 為什麼校招面試中總被問「線程與進程的區別」?我該如何回答?
    我(總是不太聰明的樣子):「限乘?」、「進什麼城(程)?」面試官:「作業系統中的進程與線程,你回去了解一下。門在左邊,記得關門。」當翻譯過來後,這兩個概念都帶了個「程」字,但進程的英文:Process,而線程的英文:Thread,好像並沒有什麼聯繫。
  • 四面阿里定級P7,復盤一下面試過程,居然如此簡單!
    10 聽說我是非科班,於是問了些排序算法面試耗時將近30分鐘。阿里技術三面:三面不是面試,而是筆試,耗時三個小時,考的是Java核心的基礎。Java 中的自增(i++)是線程安全的嘛?如何實現線程安全的自增?2. 其他常用的線程安全的類;concurrentHashMap;3. ConcurrentHashMap是如何實現線程安全的?4.
  • 面試官問我:Object o = new Object() 佔用了多少個字節?
    小小面試一下前言蜜語最近馬師傅火的不要不要的,雖然沒有搶到耗子尾汁的商標註冊權,但是必須得蹭一波馬師傅的熱度,下面就是閃電五連鞭的教學環節,你準備好了嗎!在正式內容開始前先甩兩篇關於類加載機制和內存布局的文章,因為今天的內容多少與這兩篇文章有直接的聯繫,對這方面還比較薄弱的朋友可以先看看,地址我放在下面。jvm┃java內存區域,跳槽大廠必會知識點!moon不講武德!!!一個類加載機制給面試官說蒙了!!
  • 原Boss內推阿里面試,四面面經,有個好老大真香啊
    先扯扯皮首先做一個簡單的自我介紹,主要包括學校經歷和工作經歷。我工作經歷只有兩年,大部分時間都是在做產品設計和UI/UX Design,因此隔著電話都能感受到面試官的shock。最近原來實習時候的Boss聯繫我,說他跳槽到了阿里,問我有沒有興趣面一個Java後臺開發崗位.考慮到我只工作了一年,現在去阿里肯定要降薪,因此也沒有太強烈的意願。但出於提升自我的角度考慮,咱也不想的年薪百萬,50萬也是可以的啊()就這樣參加了面試.,下面是我的面試經歷。
  • 面試懵了:StringBuilder為什麼線程不安全
    ,免費分享給大家(已修復)下一篇:2020年網際網路大廠最新面試真題以及2000道常見Java面試題(附答案)作者:千山qianshan來源:juejin.im/post/5d6228046fb9a06add4e37fe引言周五去面試又被面試的一個問題問啞巴了面試官:StringBuilder
  • 最新阿里面試回來總結分享
    高並發下有哪些常用的技術解決方案,舉三個高並發場景設計例子 說一個你對 JVM 優化的實際案例,包括實際步驟和方法 Docker 有使用過和了解嗎? 面試總結: java 的基礎知識點:主要圍繞在集合類和多線程等:ArrayList、LinkedList、HashSet、HashpMap 的數據結果,以及如何擴容、以及 ConcurrentHashMap 相關的多線程安全等。
  • 一道面試題,我把阿里面試官唬住了
    這個問題是安琪拉之前面試被問到的一個問題,正好順著上一篇文章介紹完線程調用時的用戶態和內核態的切換,後續把 Java 並發的都一起講了。面試官:聽前一個面試官說你 Java 並發這塊掌握的不錯,我們深入的交流一下;我:  看了看面試官頭部稀疏的結締組織,已然覺得這場面試不簡單,不過好在事前把安琪拉的博客看了個遍,有所準備,我回答說:咳咳,掌握的還算可以。
  • JavaScript用Math.random()生成隨機數
    基本概念顧名思義,Math.random()方法就是用於生成隨機數的,因為單詞random的意思正是「隨機的」。該方法生成的結果是 [0, 1) 範圍內的浮點數,注意這是一個左閉右開的區間,即該區間包含0而不包含1。官方文檔指出Math.random()方法生成的隨機數在該區間上要大致符合均勻分布。
  • 別再問我多線程的這些問題了
    很多同學面對多線程的問題都很頭大,因為自己做項目很難用到,但是但凡高薪的職位面試都會問到。。畢竟現在大廠裡用的都是多線程高並發,所以這塊內容不吃透肯定是不行的。使用多線程確實提高了運行的效率,但與此同時,我們也需要特別注意數據的增刪改情況,這就是線程安全問題,比如之前說過的 HashMap vs HashTable,Vector vs ArrayList。
  • 985碩,秋招面試30家企業,怒斬阿里、字節、美團offer
    6.1號開始投簡歷,7.6號開始第一場面試,9.30號收到最後一家意向書,我的秋招結束了!找工作期間薅了網上不少大佬的羊毛,特別感謝期間給予幫助的各位前輩們。在此記錄下秋招的全過程,也算是對幫助我的大佬們的回饋,十一假期期間碼字,面試問題都排在後面(先看看我是如何一點點薅羊毛的),看得出我對幫助過我的大佬們的重視!
  • 面試題:StringBuilder為什麼線程不安全?
    點擊上方「搜雲庫技術團隊」關注,選擇「設為星標」引言周五去面試又被面試的一個問題問啞巴了
  • 3個可以問面試官的問題
    今天我們來聊一下「面試的時候我們可以怎麼問問題」我們現在的面試通常都會以「你有什麼問題想問我嗎?」來作為結束。但這不意味著面試就結束了,面試官還是會根據你問的問題來考核你。所以我會建議你至少準備3個問題,你可以根據面試的實際情況去決定問哪幾個問題,因為有些問題的答案可能在面試的過程中對方就有提到,那就沒有必要再問一遍了。 第二個坑,就是問問題的環節不是給我們拿來問薪資待遇的,更加不能把它變成薪資談判。
  • 面試官:不使用synchronized和lock,如何實現一個線程安全的單例?
    面試官:除了這種以外,還有其他方式嗎?面試官:除了這種以外,還有其他方式嗎?面試官:以上幾種答案,其實現原理都是利用藉助了類加載的時候初始化單例。即藉助了ClassLoader的線程安全機制。所謂ClassLoader的線程安全機制,就是ClassLoader的loadClass方法在加載類的時候使用了synchronized關鍵字。也正是因為這樣, 除非被重寫,這個方法默認在整個裝載過程中都是同步的,也就是保證了線程安全。
  • 迷茫期後面試阿里奮發圖強8個月,如願拿到offer,定級阿里P7
    前言要說程式設計師最想要進入的大廠,阿里,騰訊,百度必定是首當其衝,而網際網路大廠必定是阿里巴巴首當其衝,今天就來分享一下我面試阿里的一些經歷與心得。不想進大廠的程式設計師不是好程式設計師!人生轉折出於對現狀的不滿,我決定要努力奮鬥了!因為覺得這樣確實是在浪費自己時間,(看著同期的同學都在茁壯成長) 與其坐以待斃,不如我自己先出去試試水,去一家能夠迅速成長的公司。
  • python面試輕鬆搞定,助你搞定面試官,內附15題及答案(08期)
    小夥伴們,有沒有發現,越是好的公司面試,面試官給你的題目,全都是考你的「基本功」。下面我給大家集齊了一些面試題,拿走,不用客氣!1.__new__和__init__的區別答:這個__new__確實很少見到,先做了解吧.1. __new__是一個靜態方法,而__init__是一個實例方法.2.