生成随机数的几种方法
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 localRandom =
new ThreadLocal() {
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
实例,然后调用 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。