当前位置:   article > 正文

Java中Random详解_java random

java random

目录

伪随机

什么是伪随机数?

Java随机数产生原理:

Java中常见生成随机数的几种方式

Math.random()

Random

Random的两种构造方法:

种子的作用数什么?

小结

ThreadLocalRandom

SecureRandom

总结

参考资料


伪随机

什么是伪随机数?

1.伪随机数是看似随机实质是固定的周期性序列,也就是有规则的随机。

2.只要这个随机数是由确定算法生成的,那就是伪随机,只能通过不断算法优化,使你的随机数更接近随机。(随机这个属性和算法本身就是矛盾的)

3.通过真实随机事件取得的随机数才是真随机数。

Java随机数产生原理:

Java的随机数产生是通过线性同余公式产生的,也就是说通过一个复杂的算法生成的。

伪随机数的不安全性:

Java自带的随机数函数是很容易被黑客破解的,因为黑客可以通过获取一定长度的随机数序列来推出你的seed,然后就可以预测下一个随机数。

Java中常见生成随机数的几种方式

Java中常见生成随机数方式通常会采用以下几类:Math.random()、Random、ThreadLocalRandom、SecureRandom 如果想要详细了解这三个类,可以查看对应Java API。

Math.random()

调用这个Math.Random()函数能够返回带正号的double值,取值范围是[0.0,1.0),在该范围内(近似)均匀分布。因为返回值是double类型的,小数点后面可以保留15位小数,所以产生相同的可能性非常小,在这一定程度上是随机数。

Random

Random r1 = new Random(); Random r2 = new Random(); Random r3 = new Random(10); Random r4 = new Random(10);

Random的两种构造方法:

Random():使用一个和当前系统时间对应的相对时间有关的数字作为种子数。Random(long seed):直接传入一个种子数。

种子的作用数什么?

种子就是产生随机数的第一次使用值,机制是通过一个函数,将这个种子的值转化为随机数空间中的某一个点上,并且产生的随机数均匀的散布在空间中。以后产生的随机数都与前一个随机数有关。

种子数只是随机算法的起源数字,和生成的随机数字的区间没有任何关系。

Random r1 = new Random(); Random r2 = new Random(); //无参构造使用的是参数作为种子数 Random r3 = new Random(100); Random r4 = new Random(100); //产生随机数调用nextXXX()方法 System.out.println(r1.nextInt(10)); System.out.println(r1.nextInt(10)); System.out.println(r2.nextInt(10)); System.out.println(r2.nextInt(10)); System.out.println("-----------------"); System.out.println(r3.nextInt(10)); System.out.println(r3.nextInt(10)); System.out.println(r4.nextInt(10)); System.out.println(r4.nextInt(10));

输出:

7 2 0 8 ----------------- 5 0 5 0

小结

  1. java.util.Random类中实现的随机算法是伪随机,也就是有规则的随机,所谓有规则的就是在给定种(seed)的区间内随机生成数字;
  2. 相同种子数的Random对象,相同次数生成的随机数字是完全相同的;
  3. Random类中各方法生成的随机数字都是均匀分布的,也就是说区间内部的数字生成的几率均等;

ThreadLocalRandom

这个类是Java7新增的类,给多线程并发生成随机数使用的。为什么ThreadLocalRandom要比Random快呢,这是因为Random在生成随机数的时候使用了CAS(compare and set),但是ThreadLocalRandom却没有使用。

下面是java.util.Random的生成随机数的方法:

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

seed 是一个全局变量:

/** * The internal state associated with this pseudorandom number generator. * (The specs for the methods in this class describe the ongoing * computation of this value.) */ private final AtomicLong seed;

多个线程同时获取随机数的时候,会竞争同一个seed,导致了效率的降低。

可见,其中通过CAS方式保证其线程安全性。这在高并发的环境中由于线程间的竞争必然带来一定的性能损耗。

ThreadLocal此时就派上用场了,ThreadLocalRandom是通过ThreadLocal改进的用于随机数生成的工具类,每个线程单独持有一个ThreadLocalRandom对象引用,这就完全杜绝了线程间的竞争问题。

另外ThreadLocalRandom的实例化比较特别,下面简单举例一下。

ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current(); int a = threadLocalRandom.nextInt(5);

由于是和线程绑定的,所以他也是从当前线程获取的。

SecureRandom

在需要频繁生成随机数,或者安全要求较高的时候,不要使用Random,这个很好理解吧,从我们最开始的介绍中可以知道,Random生成的值其实是可以预测的。

内置两种随机数算法,NativePRNG和SHA1PRNG,看实例化的方法了。通过new来初始化,默认来说会使用NativePRNG算法生成随机数,但是也可以配置-Djava.security参数来修改调用的算法。如果是/dev/[u]random两者之一就是NativePRNG,否则就是SHA1PRNG。

在 jvm 启动参数这样加就好了,-Djava.security=file:/dev/urandom。

当然还可以通过getInstance来初始化对象,有一个参数的,直接传一个算法名就行,如果不存在算法抛异常;另外有两个参数的,第二个参数还可以指定算法程序包。下面来看下实现代码。

SecureRandom secureRandom = new SecureRandom(); SecureRandom secureRandom3 = SecureRandom.getInstance("SHA1PRNG"); SecureRandom secureRandom2 = SecureRandom.getInstance("SHA1PRNG", "SUN");

当然我们使用这个类去生成随机数的时候,一样只需要生成一个实例每次去生成随机数就好了,也没必要每次都重新生成对象。另外,这个类生成随机数,首次调用性能比较差,如果条件允许最好服务启动后先调用一下nextInt()。

另外,实际上SHA1PRNG的性能将近要比NativePRNG的性能好一倍,synchronized的代码少了一半,所以没有特别重的安全需要,尽量使用SHA1PRNG算法生成随机数。

总结

1、单机中如果对安全性要求不高的情况下,使用 Random;对安全性要求高,就用 SecureRandom;

SecureRandom里有两种算法,SHA1PRNG 和 NativePRNG,SHA1PRNG的性能好,但是NativePRNG的安全性高。

2、Random 是线程安全的,用CAS来保持,但是性能比不高,所以多线程中,尽量使用 java并发包里的 ThreadLocalRandom,

避免了线程之间的竞争导致的性能问题

参考资料

Java中生成随机数Random、ThreadLocalRandom、SecureRandom、Math.random()

为什么说Java中的随机数都是伪随机数?

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/106950
推荐阅读
相关标签
  

闽ICP备14008679号