【小家java】Java中Random ThreadLocalRandom 设置随机种子获取随机数精讲(下)

简介: 【小家java】Java中Random ThreadLocalRandom 设置随机种子获取随机数精讲(下)

二、Random(long seed) 有参构造方法(设置种子)


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);
        }
    }
    private static long initialScramble(long seed) {
        return (seed ^ multiplier) & mask;
    }


从源码可以看出,我们可以手动的给传递一个种子进去,然后通过线形算法计算我们的种子,构造一个long值即可。


  public static void main(String[] args) {
        Random r = new Random(1000);
        for (int i = 1; i < 4; i++) {
            System.out.println("第" + i + "次:" + r.nextInt());
        }
    }
输出:
第1次:-1244746321
第2次:1060493871
第3次:-1826063944
第二次输出:
第1次:-1244746321
第2次:1060493871
第3次:-1826063944


我们发现,不管运行多少次,规律都是一模一样的,再看这个例子


public static void main(String[] args) {
        Random r = new Random(1000);
        for (int i = 1; i < 4; i++) {
            System.out.println("第" + i + "次:" + r.nextInt());
        }
        Random r2 = new Random(1000);
        for (int i = 1; i < 4; i++) {
            System.out.println("第" + i + "次:" + r2.nextInt());
        }
    }
输出:
第1次:-1244746321
第2次:1060493871
第3次:-1826063944
第1次:-1244746321
第2次:1060493871
第3次:-1826063944


我们发现,哪怕是两个random对象,只要种子一样,输出的随机数都是一样的,所以一定要慎用种子啊。


这里同样的代码,只要你不换机器,运行多少次都是相同的。但是如果换一台机硬件机器,就不同了哟。需要了解这里面的原理。种子不同,产生不同的随机数。种子相同,即使实例不同也产生相同的随机数。


new Random(1000)显式地设置了随机种子为1000,运行多次,虽然实例不同,但都会获得相同的三个随机数。所以,除非必要,否则不要设置随机种子。


虽然二者都是伪随机,但是,无参数构造方法(不设置种子)具有更强的随机性,能够满足一般统计上的随机数要求。使用有参的构造方法(设置种子)无论你生成多少次,每次生成的随机序列都相同,名副其实的伪随机!!


最后再来简单对比一下这两个随机函数到底的特点:


1.java.Math.Random()实际是在内部调用java.util.Random()的,它有一个致命的弱点,它和系统时间有关,也就是说相隔时间很短(短到种子相同)的两个random比如: double a = Math.random();double b = Math.random(); 即有可能会得到两个一模一样的double。


2.java.util.Random()在调用的时候可以实现和java.Math.Random()一样的功能,而且他具有很多的调用方法,相对来说比较灵活。所以从总体来看,使用java.util.Random()会相对来说比较灵活一些。


写到最后:Random和ThreadLocalRandom的用法和区别


Random:生产一个伪随机数(通过相同的种子,产生的随机数是相同的)。


ThreadLocalRandom:是java7新增类,是Random的子类,在多线程并发情况下,ThreadLocalRandom相对于Random可以减少多线程资源竞争,保证了线程的安全性。public class ThreadLocalRandom extends Random因为构造器是默认访问权限,只能在java.util包中创建对象,故提供了一个方ThreadLocalRandom.current()用于返回当前类的对象.

package java.util.concurrent;
public class ThreadLocalRandom extends Random {}


我们发现他在concurrent包下,所以他肯定就是为并发而生的。下面卡个使用案例:


  public static void main(String[] args) {
        ThreadLocalRandom threadRandom = ThreadLocalRandom.current();
        System.out.println(threadRandom.nextInt(10));
        System.out.println("-----------产生两个数之间的随机数----------------");
        System.out.println(threadRandom.nextInt(10, 100));
        System.out.println("---------------------------");
        //随机生成UUID
        String uuid = UUID.randomUUID().toString();
        System.out.println(uuid);
    }
输出:
3
-----------产生两个数之间的随机数----------------
62
---------------------------
cb688869-24a3-457f-abc9-a2e0687c5e66


下面从性能方面,来看看ThreadLocalRandom 这个哥们的优势


ThreadLocalRandom类是JDK7在JUC包下新增的随机数生成器,它解决了Random类在多线程下多个线程竞争内部唯一的原子性种子变量而导致大量线程自旋重试的不足。


先给出个结论:ThreadLocalRandom使用ThreadLocal的原理,让每个线程内持有一个本地的种子变量,该种子变量只有在使用随机数时候才会被初始化,多线程下计算新种子时候是根据自己线程内维护的种子变量进行更新,从而避免了竞争。


因此,互联网分布式环境下,建议使用此类来代替Random类来提高效率


至于具体原因,从源码级别分析的内容,这里推荐一篇文章参考:ThreadLocalRandom类原理剖析


Random参考:JAVA的Random类介绍


自1.0就已经存在,是一个线程安全类,理论上可以通过它同时在多个线程中获得互不相同的随机数,这样的线程安全是通过AtomicLong实现的。

Random使用AtomicLong CAS(compare and set)操作来更新它的seed,尽管在很多非阻塞式算法中使用了非阻塞式原语,CAS在资源高度竞争时的表现依然糟糕,后面的测试结果中可以看到它的糟糕表现。


使用一个普通的long而不是使用Random中的AtomicLong作为seed

不能自己创建ThreadLocalRandom实例,因为它的构造函数是私有的,可以使用它的静态工厂ThreadLocalRandom.current()

它是CPU缓存感知式的,使用8个long虚拟域来填充64位L1高速缓存行


相关文章
|
1月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
67 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
1月前
|
Java Linux iOS开发
如何设置 Java 的环境变量
设置Java环境变量是使用Java开发工具和运行Java程序的前提。主要步骤包括:安装JDK,配置系统环境变量中的JAVA_HOME、PATH和CLASSPATH,确保命令行可直接调用javac和java命令。
|
1月前
|
安全 Java Linux
java程序设置开机自启
java程序设置开机自启
|
1月前
|
Java
java的Random类和Arrays.sort类使用实例
java的Random类和Arrays.sort类使用实例
10 0
|
1月前
|
Java
java值random类的使用
java值random类的使用
11 0
|
1月前
|
Java
java的Math类和random类
java的Math类和random类
14 0
|
3月前
|
JSON Java API
【Azure API 管理】通过Java APIM SDK创建一个新的API,如何为Reqeust的Representation设置一个内容示例(Sample)?
【Azure API 管理】通过Java APIM SDK创建一个新的API,如何为Reqeust的Representation设置一个内容示例(Sample)?
|
11天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
7天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
28 9
|
10天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####