Java 隨機數(Random VS SecureRandom)

生成隨機數的幾種方法

  • Math.random()一隨機數
  • java.util.Random僞隨機數(線性同餘法生成)
  •  java.util.concurrent.ThreadLocalRandom 工具類
  • java.security.SecureRandom 真隨機數
  •  Apache Commons-Lang 包中的 RandomStringUtils 類

Math.random():(產生[0,1)範圍的double隨機數)

源碼

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


private static Random randomNumberGenerator;//僞隨機數生成器

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

源碼分析:當第一次調用Math.random()方法時,會生成僞隨機數生成器randomNumberGenerator,之後再調用此方法將不再生成僞隨機數生成器,而是繼續沿用此僞隨機數生成器。此種生成隨機數的方式是線程安全的,但是在多線程下可能性能比較低。

java.util.Random工具類

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

在注重信息安全的應用中,不要使用 LCG 算法生成隨機數,請使用 SecureRandom

源碼:

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

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

源碼分析:Random類默認使用當前系統時鐘作爲種子,只要種子一樣,產生的隨機數也一樣。種子確定,隨機算法也確定,得出的隨機數也是確定的。

 java.util.concurrent.ThreadLocalRandom 工具類

ThreadLocalRandom 是JDK 7之後繼承至java.util.Random

源碼:

public static ThreadLocalRandom current() {
        return localRandom.get();
    }

private static final ThreadLocal<ThreadLocalRandom> localRandom =
        new ThreadLocal<ThreadLocalRandom>() {
            protected ThreadLocalRandom initialValue() {
                return new ThreadLocalRandom();
            }
    };
//ThreadLocalRandom繼承於Random
ThreadLocalRandom() {
        super();   //java.util.Random的構造方法
        initialized = true;
    }

使用:

package com.example.random;

import java.util.concurrent.ThreadLocalRandom;

public class ThreadLocalRandomTest {
	
	public static void main(String[] args) {
		new MyThread().start();
		new MyThread().start();
	}
}

class MyThread extends Thread{
	public void run(){
		for(int i=0;i<10;i++){
			System.out.println(Thread.currentThread().getName()+":"+ThreadLocalRandom.current().nextDouble());
		}
	}
}

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

 java.Security.SecureRandom(繼承至java.util.Random)

使用:

//採用SecureRandom 生成6位驗證碼
private static String getRandom6() throws NoSuchAlgorithmException {
		
		SecureRandom random= SecureRandom.getInstance("SHA1PRNG");
		int verifiCode = (int)Math.ceil(random.nextFloat()*1000000);
		String verifiCodeStr = String.valueOf(verifiCode);
                //處理產生的隨機數不及6位的情況
		while(verifiCodeStr.length()<6){
			verifiCode = (int)Math.ceil(random.nextFloat()*1000000);
			verifiCodeStr = String.valueOf(verifiCode);
		}
		return verifiCodeStr;
	}

SecureRandom提供加密的是強隨機數生成器,種子是不可預知的,產生的隨機數也是不確定。

從理論上來說計算機產生的隨機數都是僞隨機數,那麼如何產生高強度的隨機數?

答:產生高強度的隨機數,有兩個重要的因素:種子和算法。算法可以有很多種, 如何選擇種子是非常關鍵的因素。如Random,它的種子是System.currentTimeMillis().所以它的隨機數都是可以預測的。那麼如何得到一個近似隨機的種子?可以利用計算機收集的各種信息,如鍵盤輸入時間,cpu時鐘,內存使用狀態,硬盤空閒空間,IO延時,進程的數量,線程數量等來得到以及近似隨機的種子。如此,除了理論上有破解的可能,實際上基本沒有被破解的可能。事實表明,現在高隨機數的生成都是這樣實現的。

 Apache Commons-Lang 包中的 RandomStringUtils 類

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

示例:

package com.example.random;

import org.apache.commons.lang.RandomStringUtils;

public class RandomStringUtilsTest {
	
	public static void main(String[] args) {
		
		//生成64位長度的數字字符串
		String result = RandomStringUtils.random(64,false,true);
		System.out.println("數字random:"+result);
		
		//生成64位的字母字符串
		result=RandomStringUtils.randomAlphabetic(64);
		System.out.println("字母random:"+result);
		
		//生成32位ASCII字符串
		result=RandomStringUtils.randomAscii(32);
		System.out.println("ASCII random:"+result);
		
		//根據指定字符生成32位隨機字符串
		 result = RandomStringUtils.random(32, 0, 20, true, true, "qw32rfHIJk9iQ8Ud7h0X".toCharArray());
	     System.out.println("random = " + result);
				
	}

}

拓展:生成java隨機字符串UUID。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章