`
QING____
  • 浏览: 2250669 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JAVA中Random分析

 
阅读更多

    JAVA中提供了几个常用的用于生成随机数(流,JDK 8支持)的API:Random、ThreadLocalRandom、SecureRandom、SplittableRandom;这几个类在使用场景上,稍微有些区分。

 

一、Random

    1、生成伪随机数(流),使用48位种子,使用线性同余公式进行修改。可以通过构造器传入初始seed,或者通过setSeed重置(同步);默认seed生成主导变量为系统时间的纳秒数。

    2、如果两个(多个)不同的Random实例,使用相同的seed,按照相同的顺序调用相同方法,那么它们得到的数字序列也是相同的。这种设计策略,既有优点也有缺点,优点是“相同seed”生成的序列是一致的,使过程具有可回溯和校验性(平台无关、运行时机无关);缺点就是,这种一致性,潜在引入其“可被预测”的风险。

    3、Random的实例是线程安全的。 但是,跨线程并发使用相同的java.util.Random实例可能会遇到争用,从而导致性能稍欠佳(nextX方法中,在对seed赋值时使用了CAS,测试结果显示,其实性能损耗很小)。 请考虑在多线程设计中使用ThreadLocalRandom。同时,我们在并发环境下,也没有必要刻意使用多个Random实例。

    4、Random实例不具有加密安全性。 相反,请考虑使用SecureRandom来获取加密安全的伪随机数生成器,以供安全敏感应用程序使用。

 

    Randoms是最常用的随机数生成类,适用于绝大部分场景。

Random random = new Random(100);
System.out.println(random.nextInt(10) + "," + random.nextInt(30) + "," + random.nextInt(50));

random = new Random(100);
System.out.println(random.nextInt(10) + "," + random.nextInt(30) + "," + random.nextInt(50));

random = new Random(100);
System.out.println(random.nextInt(10) + "," + random.nextInt(30) + "," + random.nextInt(50));

    上述三个不同的random实例,使用了相同的seed,调用过程一样,其中产生的随机数序列也是完全一样的。多次执行结果也完全一致,简单而言,只要初始seed一样,即使实例不同,多次运行它们的结果都是一致的。

    如果Random构造器中不指定seed,而是使用默认的系统时间纳秒数作为主导变量,三个random实例执行的结果是不同的。多次执行结果也不一样。由此可见,seed是否具有随机性,在一定程度上,也决定了Random产生结果的随机性。

 

    所以,在分布式或者多线程环境下,如果Random实例处于代码一致的tasks线程中,可能这些分布式进程或者线程,产出的序列值可能是一样的。所以,这也是在JDK 7引入ForkJoin的同时,也引入了ThreadLocalRandom类。

 

二、ThreadLocalRandom

    随机数生成器隔离到当前线程,此类继承自java.util.Random。 与Math类使用的全局Random生成器一样,ThreadLocalRandom使用内部生成的种子进行初始化,否则可能无法修改。

    在并发程序中使用ThreadLocalRandom而不是共享Random对象,通常会更少的开销和争用。 当多个任务(例如,每个ForkJoinTask)在线程池中并行使用随机数时,使用ThreadLocalRandom是特别合适的。

 

    此类的用法通常应为以下形式:ThreadLocalRandom.current().nextX()。 切记,在多个线程中不应该共享ThreadLocalRandom实例,即我们遵循上述方法调用方式来获取随机数。

 

    ThreadLocalRandom初始化过程也被关闭,所以无法通过构造器设定seed,此外其setSeed方法也被重写而不支持(抛出异常),默认情况下,每个ThreadLocalRandom实例的seed主导变量值为系统时间(纳秒):

private static long initialSeed() {
    String sec = VM.getSavedProperty("java.util.secureRandomSeed");
    if (Boolean.parseBoolean(sec)) {
        byte[] seedBytes = java.security.SecureRandom.getSeed(8);
        long s = (long)(seedBytes[0]) & 0xffL;
        for (int i = 1; i < 8; ++i)
            s = (s << 8) | ((long)(seedBytes[i]) & 0xffL);
        return s;
    }
    return (mix64(System.currentTimeMillis()) ^
            mix64(System.nanoTime()));
}

 

   根据其初始化seed的实现,我们也可以通过JVM启动参数增加“-Djava.util.secureRandomSeed=true”,此时初始seed变量将不再是系统时间,而是由SecureRandom类生成一个随机因子,以此作为ThreadLoalRandom的初始seed,相对更加安全。

 

    从源码中,我并没有看到Thread-ID作为变量生成seed,而且nextX方法中随机数生成算法也具有一致性,这意味着,如果多个线程初始ThreadLocalRandom的时间完全一致,在调用方法和过程相同的情况下,产生的随机序列也是相同的;在一定程度上“-Djava.util.secureRandom=true”可以规避此问题。

 

    ThreadLocalRandom并没有使用ThreadLocal来支持内部数据存储等,而是直接使用UnSafe操作当前Thread对象引用中seed属性的内存地址并进行数据操作,我比较佩服SUN的这种巧妙的做法。

 

三、SecureRandom

    继承自Random,该类提供加密强随机数生成器(RNG),加密强随机数最低限度符合FIPS 140-2“加密模块的安全要求”。 此外,SecureRandom必须产生非确定性输出。 因此,传递给SecureRandom对象的任何种子材料必须是不可预测的,并且所有SecureRandom输出序列必须具有加密强度。(官文,其实我也一知半解)

 

    SecureRandom默认支持两种RNG加密算法实现:

    1)"SHA1PRNG"算法提供者sun.security.provider.SecureRandom

    2)"NativePRNG"提供者sun.security.provider.NativePRNG

 

    默认情况下,是“SHA1PRNG”,即SUN提供的实现。此外可以通过“-Djava.security=file:/dev/urandom”(推荐)或者“-Djava.security=file:/dev/random”指定使用linux本地的随机算法,即NativePRNG;其中“/dev/random”与“/dev/urandom”在不同unix-*平台中实现有所不同,性能也有所差异,建议使用“/dev/urandom”。请参考wiki:/dev/random

dev/random的一个副本是/dev/urandom (”unlocked”,非阻塞的随机数发生器),它会重复使用熵池中的数据以产生伪随机数据。这表示对/dev/urandom的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random的。它可以作为生成较低强度密码的伪随机数生成器,不建议用于生成高强度长期密码。

 

   算法的内部实现,比较复杂;本人测试,其实性能差不不太大(JDK 8环境)。SecureRandom为线程安全。

 

    从输出结果上分析,无论是否指定SecureRandom的初始seed,单个实例多次运行的结果也完全不同;多个不同的SecureRandom实例无论是否指定seed,即使指定一样的初始seed,同时运行的结果也完全不同。

 

    SecureRandom继承自Random,但是对nextX方法中的底层方法进行的重写覆盖,不过仍然基于Random的CAS且SecureRandom的底层方法还使用的同步,所以在并发环境下,性能比Random差了一些。

 

四、SplittableRandom

    JDK 8 新增的API,主要适用于Fork/join形式的跨线程操作中,其未继承java.util.Random类。

    1、在seed生成和使用层面,实现与ThreadLocalRandom一致,但其并没有继承ThreadLocalRandom。

    2、具有相同seed的不同SplittableRandom实例或者同一个SplittableRandom,多次运行结果是一致的。

    3、非线程安全,不能被并发使用。(不会报错,但是并发时可能多个线程同时得到相同的随机数)

    4、同ThreadLocalRandom,对“-Djava.util.secureRandom=true”参数支持,但是只有使用默认构造器的时候,才会使用SecureRandom辅助生成初始seed。即不指定初始seed时,同一个SplittableRandom实例多次运行,或者不同的实例运行,结果是不同的。

    5、split()方法,构造并返回与当前实例共享不可变状态的新SplitableRandom实例。 然而,以非常高的概率,由两个对象共同生成的值具有与使用单个SplittableRandom对象的单个线程生成相同数量的值相同的统计特性。需要注意,split产生的新SplittableRandom实例,与原实例并不存在内部数据的并发竞争,也不会交替或者延续原实例的随机数生成序列(即两个实例产出随机序列的一致性,与原实例没有关系,只是在统计值层面更加接近);但是代码一致性的情况下,多次运行,其随机数序列的结果总是一致的(假如初始seed是指定的,而非默认),这一点与Random、ThreadLocalRandom一样。

public SplittableRandom split() {
    return new SplittableRandom(nextLong(), mixGamma(nextSeed()));
}

 

    代码样例

System.out.println("第一段");
SplittableRandom random = new SplittableRandom(100);
Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        SplittableRandom _random = random.split();
        for (int i=0; i < 5; i++) {
            System.out.println("---" + _random.nextInt(100));
        }
    }
});
thread.start();
thread.join();
for (int i=0; i < 5; i++) {
    System.out.println("+++" + random.nextInt(100));
}

System.out.println("第二段");
SplittableRandom _random = new SplittableRandom(100);
for (int i=0; i < 10; i++) {
    System.out.println("+++" + _random.nextInt(100));
}

 

第一段
---71
---85
---10
---60
---98
+++44
+++87
+++77
+++67
+++72


第二段
...92
...30
...44
...87
...77
...67
...72
...23
...9
...64

 

   从执行结果上看,split产生的random实例与原实例执行结果上没有相似之处;但是不同SplittableRandom实例(无论是否执行过split),其产出随机数序列是一致的。

 

五、性能检测(简析,基准:100000随机数,单线程)

    1、Random:2毫秒

    2、ThreadLocalRandom:1毫秒

    3、SecureRandom

        1)默认算法,即SHAR1PRNG:80毫秒左右。

        2)NativePRNG:90毫秒左右。

    4、SplittableRandom:1毫秒

分享到:
评论

相关推荐

    了解Java:RandomAccess

    1. Java中的RandomAccess接口和RandomAccessFile类的介绍。 2. RandomAccessFile的用法,包括创建、打开、关闭文件,以及如何进行随机读写操作。 3. 如何通过RandomAccessFile高效处理大文件和定位文件特定位置。 4....

    Java写的数值分析中的一些程序

    这个压缩包中的“Math”文件很可能包含了一系列与数值分析相关的Java程序,虽然没有注释,但我们可以根据Java的常用库和数值分析的基本概念来解析这些程序可能涉及的知识点。 首先,数值分析是应用数学的一个分支,...

    java试验,random函数调用等

    在Java编程语言中,`Random`类是生成随机数的核心工具,它位于`java.util`包下。这个类提供了各种方法来生成不同类型的随机数,包括整数、浮点数以及在特定范围内的数字。在"java试验,random函数调用等问题"中,...

    java中随机输出1到32

    在Java编程语言中,随机数生成是...通过以上分析,我们不仅理解了如何在Java中实现随机输出1到32的功能,还了解了Java中随机数生成的基本原理,以及如何优化和扩展代码,这对于提升代码质量和功能多样性具有重要意义。

    java 中RandomAccess接口源码分析

    java 中RandomAccess接口源码分析 java 中RandomAccess接口源码分析是java.util包中的一个接口,该接口的主要作用是标记List实现是否支持快速的随机访问。通过实现这个接口,List实现可以表明它们支持快速的随机...

    Java重启式随机游走代码(Random Walk with Restart,RWR)

    随机游走是网络分析中的一个基础概念,它模拟了一个粒子在网络中从一个节点移动到另一个节点的过程。在每次步进中,粒子根据邻接节点的概率分布选择下一个节点。而RWR则引入了重启概率C(通常称为重置参数),使得...

    javarandom源码-java_random:我创建的一些随机Java源代码

    在Java编程语言中,`Random`类是Java.util包下的一个核心类,它提供了一系列方法来生成各种类型的随机数。这个类广泛应用于各种场景,包括模拟、加密、游戏开发等。`java_random`项目看起来是一个个人创建的开源项目...

    CRF.rar_CRF_CRF JAVA_random

    总的来说,CRF是一种强大的序列标注工具,通过在Java中实现,可以灵活地应用于各种数据集,并与其他NLP工具结合,构建复杂的文本分析系统。在理解其基本原理的同时,掌握如何在Java环境下利用CRF进行建模和优化,...

    java模拟掷骰子2个源码示例

    首先,Java中的`Random`类是生成随机数的核心工具。在模拟掷骰子时,我们通常会创建一个`Random`对象,然后调用其`nextInt()`方法来生成介于0(包含)和指定上限(不包含)之间的随机整数。对于一个六面的骰子,我们...

    JAVA基础随机整数的生成

    在JAVA编程中,随机整数的生成是一项基本但关键的技术,广泛应用于游戏、加密、算法测试等多种场景。本文将深入探讨JAVA中生成随机整数的有效方法,对比不同策略的优劣,并推荐最佳实践。 #### 一、随机数生成的...

    AHP.zip_AHP java code_ahp_ahp java_ahp层次分析java_层次分析法

    在IT行业中,尤其是在软件工程和系统分析中,AHP常被用来解决项目优先级排序、需求分析、系统设计等复杂问题。 AHP.zip文件包含了一个使用Java语言实现的层次分析法源代码,名为"AHP.war",这是一个Web应用程序的...

    java随机数

    本文将深入探讨Java中的随机数生成机制,并对各种方法进行对比分析。 #### Java随机数生成概述 Java提供两种主要的方式来生成随机数:`java.lang.Math.random()` 和 `java.util.Random` 类。这两种方法各有优劣,...

    Java猜拳游戏 Java源代码

    3. **随机数生成**:Java的`java.util.Random`类用于生成随机数,以决定电脑的出拳。通过调用`nextInt()`方法并进行适当转换,可以确保电脑出拳的随机性。 4. **条件判断语句**:在比较用户和电脑的出拳时,需要...

    java开发技术调用rendom函数,随机生成32位不重复的字符

    在这里,我们将讨论如何使用Java中的Random函数来生成32位不重复的字符。 一、Java中的Random函数 Java中的Random函数是用来生成随机数的。Random函数可以生成bool、byte、char、double、float、int、long、short...

    Java中不重复随机数的生成与分析.pdf

    Java中不重复随机数的生成与分析 Java 中不重复随机数的生成与分析是软件开发中的一个重要问题。随机数以其不确定性和偶然性等特点,因此在软件开发中得到了广泛应用。例如,在软件系统的测试中,用于产生具有普遍...

    java生成随机数的常用方法分析

    在 Java 编程中,生成随机数是非常常见的需求,例如在测试、模拟、游戏等场景中。随机数可以使用多种方法生成,本文将对 Java 生成随机数的常用方法进行分析。 1. 使用 Math.random() 生成随机数 Math.random() ...

    Java中随机数的生成、分析及其应用

    ### Java中随机数的生成、分析及其应用 #### 引言 在计算机科学领域中,随机数的应用极为广泛,从游戏开发、加密技术到科学研究等多个领域都有涉及。根据生成方式的不同,随机数主要分为两类:真正的随机数和伪随机...

    java中随机函数的使用.doc

    2. 数据分析:在数据分析中,随机函数可以用于生成随机样本,用于数据挖掘、机器学习等。 3. 模拟仿真:在模拟仿真中,随机函数可以用于生成随机事件、随机变量等。 三、Java 中随机函数的注意事项 在使用 Java 中...

    层次分析法源代码

    在本压缩包文件中,包含了一个用Java实现的层次分析法的源代码,用于判断判断矩阵是否符合要求,并计算一致性比率(Consistency Ratio,简称CR)。 在AHP中,首先需要构建层次结构模型,包括目标层、准则层和方案层...

    Java Random.nextInt()方法原理解析

    Java中的`Random`类是用于生成随机数的工具类,`nextInt()`方法是其核心功能之一,用于生成指定范围内的整数。在这个方法的解析中,我们将深入理解`nextInt()`的工作原理以及种子(seed)的概念。 `nextInt(int n)`...

Global site tag (gtag.js) - Google Analytics