java伪随机数生成器

news2024/12/23 11:22:15

关于随机数的基本概念

1、对随机数性质分类:

  • 随机性:符合该性质的叫弱伪随机数。这种随机数仅可以用于一般应用,无法用在密码学,例如java中的java.util.Random类
  • 不可预测性:符合该性质的叫强伪随机数。在密码学中,随机数除了随机性还需要不可预测性,也就是无法通过历史数据推测下一个随机数
  • 不可重复性:符合该性质的叫真随机数。仅靠软件无法生成不可重复的随机数,这是因为计算机软件仅具备有限内部状态,只要内部状态相同,必然生成同一个数,这也叫做周期。要满足不可重复性,只能通过物理现象获取,比如温度、声音等,这些需要硬件支持,比如英特尔CPU中提供了一个随机数生成器。

上述性能越往下越严格,且具有包含性质:比如不可重复性一定具有随机性和不可预测性;不可预测性一定具有随机性。

说明:反复投骰子生成的数列,具有不可重复性。

2、总结:

随机数可以通过硬件、软件来生成。通过硬件生成的随机数,例如利用传感器收集的热量、声音等具有无法预测不重复的自然现象,称作真随机数,也叫随机数生成器(Random Number Generator RNG)。

通过软件生成的随机数,叫做为随机数(Pseudo Random Number Generator PRNG)。因为软件状态有限,一定具有周期性。为随机数分为:弱为随机和强为随机。

接下来,我们介绍java中的伪随机数使用。

Random介绍

在JDK7之前java.util.Random是使用比较广泛的随机数生成工具类,另外java.lang.Math中的随机数生成也是使用的java.util.Random的实例。Random类使用的是线性同余算法生成随机数,该算法是一种弱伪随机数算法,不具备不可预测性,所以不能用于密码学。

1、Random类是线程安全的:

Random随机数生成是和种子seed有关,而为了保证线程安全性,Random通过CAS机制来保证线程安全性。从next()方法中我们可以看到seed是通过不断的CAS来进行修改值的。如果在高并发的场景下,那么可能会导致CAS失败,从而导致不断的自旋,这样可能会导致CPU过高。

此外,在创建Random的时候也是用CAS:

这里也可以看到,默认Random的种子适合当前时间有关的,当然也可以在构造函数中指定seed。

 2、使用

Random random = new Random();
random.nextInt(); //返回[0, max)的随机正整数
random.nextInt(10); //返回[0,10)的随机正整数
        
double rd1 = Math.random(); //返回[0.0,1.0)的随机小数

ThreadLocalRandom介绍

ThreadLocalRandom继承Random,也是弱伪随机数。它主要解决了Random在高并发环境下随机数生成性能问题。

1、ThreadLocalRandom为什么线程安全?

和ThreadLocal原理类似,它将随机种子保存在当前Thread对象的threadLocalRandomSeed变量中,这样每个线程都有自己的随机种子,实现了线程级别的隔离,所以ThreadLocalRandom也并不需要像Random通过自旋锁和cas来保证随机种子的线程安全性。在高并发的场景下,效率也会相对较高。

2、源码实现:

ThreadLocalRandom大量使用了unsafe方法来直接通过地址操作变量。关于unfafe用法见:3.0 Java魔法类:Unsafe应用解析.note

1)种子定义在Thread类,在ThreadLocalRandom定义的SEED表示Thread类的threadLocalRandomSeed属性的内存偏移量

ThreadLocalRandom.java
private static final sun.misc.Unsafe UNSAFE;
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;
static {
    try {
        UNSAFE = sun.misc.Unsafe.getUnsafe();
        Class<?> tk = Thread.class;
        SEED = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSeed"));
        PROBE = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomProbe"));
    } catch (Exception e) {
        throw new Error(e);
    }
}

Thread.java
@sun.misc.Contended("tlr")
//当前Thread的随机种子 默认值是0
long threadLocalRandomSeed;
@sun.misc.Contended("tlr")
//用来标志当前Thread的threadLocalRandomSeed是否进行了初始化 0代表没有,非0代表已经初始化 默认值是0
int threadLocalRandomProbe;

说明:其中 @sun.misc.Contende 是为了避免伪共享。

2)当线程调用ThreadLocalRandom的current方法,ThreadLocalRandom负责初始化调用线程的threadLocalRandomSeed变量,也就是初始化种子;

public static ThreadLocalRandom current() {
        // 获取当前线程的
        if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
            localInit();
        return instance;
}

static final void localInit() {
        int p = probeGenerator.addAndGet(PROBE_INCREMENT);
        int probe = (p == 0) ? 1 : p; // skip 0
        long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
        Thread t = Thread.currentThread();
        UNSAFE.putLong(t, SEED, seed);
        UNSAFE.putInt(t, PROBE, probe);
}

说明:通过unsafe的putLong方法根据对象属性偏移地址直接操作内存。

3)当调用ThreadLocalRandom的nextInt方法时候,实际上是获取当前线程的threadLocalRandomSeed变量作为当前种子来计算新的值,然后更新新的种子到当前线程的threadLocalRandomSeed变量。(线性同余算法:根据种子计算随机数,然后将随机数更新成新的种子供下次计算):

public int nextInt(int bound) {
        if (bound <= 0)
            throw new IllegalArgumentException(BadBound);
        int r = mix32(nextSeed());
        int m = bound - 1;
        if ((bound & m) == 0) // power of two
            r &= m;
        else { // reject over-represented candidates
            for (int u = r >>> 1;
                 u + m - (r = u % bound) < 0;
                 u = mix32(nextSeed()) >>> 1)
                ;
        }
        return r;
}
final long nextSeed() {
        Thread t; long r; // read and update per-thread seed
        UNSAFE.putLong(t = Thread.currentThread(), SEED,
                       r = UNSAFE.getLong(t, SEED) + GAMMA);
        return r;
}

并发高的情况下,试试用ThreadLocalRandom来生成随机数 - 腾讯云开发者社区-腾讯云

3、说明:

在ssh架构中,无论是Random、ThreadLocalRandom,都不能放在全局变量中定义,都应该放到方法中去定义、初始化。否则就有可能不同线程使用了相同的种子,这样创建出来的随机数序列可能是一样的。即:

java.Security.SecureRandom介绍

也是继承java.util.Random,该类提供了强伪随机数算法,可以在密码学中使用。

1、Linux 中随机数是如何产生的呢 PRNG(Pseudo-Random Number Generator)?

Linux 内核采用熵来描述数据的随机性,熵(entropy)是描述系统混乱无序程度的物理量,熵越大则说明该系统的有序性越差。内核维护了一个熵池用来收集来自设备驱动程序和其它来源的环境噪音。内核中PRNG 为一个字符设备 random,它提供了 2 个字符设备供用户态进程使用——/dev/random 和/dev/urandom:

  • /dev/random 适用于对随机数质量要求比较高的请求,在熵池中数据不足时, 读取 dev/random 设备时会返回小于熵池噪声总数的随机字节。/dev/random 可生成高随机性的公钥或一次性密码本。若熵池空了,对/dev/random 的读操作将会被阻塞,直到收集到了足够的环境噪声为止。这样的设计使得/dev/random 是真正的随机数发生器,提供了最大可能的随机数据熵。
  • /dev/urandom,非阻塞的随机数发生器,它会重复使用熵池中的数据以产生伪随机数据。这表示对/dev/urandom 的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random 的。它可以作为生成较低强度密码的伪随机数生成器,对大多数应用来说,随机性是可以接受的。

2、获取SecureRandom实例方法:

有两种方式获取SecureRandom实例:

  • SecureRandom random = new SecureRandom(); //也可以通过参数指定种子,但不建议这么做。
  • Random random = SecureRandom.getInstanceStrong();

这两种方式的区别:在linux下,第一种采用的是/dev/urandom作为伪随机数发生器;后者采用的是/dev/random作为伪随机数发生器。所以,后者可以能会因为系统的熵池数据不足导致其调用nextInt等方法获取随机数时阻塞。

1)实验:

import java.security.SecureRandom;
import java.util.Random;
import java.security.NoSuchAlgorithmException;
public class RandomTest {
    public static void main(String... str) throws NoSuchAlgorithmException {
        //SecureRandom random = new SecureRandom();
        Random random = SecureRandom.getInstanceStrong();
        int out = 0;
        for (int i = 0; i < 1<<20 ; i++) {
            out ^= random.nextInt();
        }
        System.out.println(out);
    }
}

该程序分别用两种不同方式创建SecureRandom实例,然后产生1048576次随机数。在linux上经过测试发现:使用第6行方式创建SecureRandom实例,程序在2s内执行完;如果使用第7行方式,程序会阻塞。这也验证了上面的结论。

2)通过strace跟踪系统调用:

strace命令可以跟踪linux的系统调用。所以我们对上面的程序进行如下运行:

A、使用第6行方式创建SecureRandom实例,然后javac编译,通过如下方式运行:

javac RandomTest.java
strace -ff -o ./c.strace  java RandomTest
1839562559
#会在当前目录产生大量的strace文件,通过执行如下命令搜索"dev/random"所在的文件
grep "dev" -r -n ./
./c.strace.22057:6:openat(AT_FDCWD, "/sys/devices/system/cpu", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
./c.strace.22057:966:access("/dev/random", R_OK)             = 0
./c.strace.22057:967:access("/dev/random", R_OK)             = 0
./c.strace.22057:968:access("/dev/urandom", R_OK)            = 0
./c.strace.22057:969:open("/dev/random", O_RDONLY)           = 5
./c.strace.22057:970:fstat(5, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 8), ...}) = 0
./c.strace.22057:971:open("/dev/urandom", O_RDONLY)          = 6
./c.strace.22057:972:fstat(6, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 9), ...}) = 0
./c.strace.22057:973:access("/dev/random", R_OK)             = 0
./c.strace.22057:974:access("/dev/random", R_OK)             = 0
./c.strace.22057:975:open("/dev/random", O_RDONLY)           = 7
./c.strace.22057:976:fstat(7, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 8), ...}) = 0
./c.strace.22057:977:open("/dev/random", O_RDONLY)           = 8
./c.strace.22057:978:fstat(8, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 8), ...}) = 0
./c.strace.22057:979:access("/dev/urandom", R_OK)            = 0
./c.strace.22057:980:access("/dev/urandom", R_OK)            = 0
./c.strace.22057:981:open("/dev/urandom", O_RDONLY)          = 9
./c.strace.22057:982:fstat(9, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 9), ...}) = 0
./c.strace.22057:985:open("/dev/urandom", O_RDONLY)          = 10
./c.strace.22057:986:fstat(10, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 9), ...}) = 0
./c.strace.21889:96:fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
./c.strace.22065:10:open("/sys/devices/system/cpu/online", O_RDONLY|O_CLOEXEC) = 3

打开文件

可以看到,虽然SecureRandom.java底层会打开/dev/random和/dev/urandom这两个文件,但最终读取的却是6号,也就是/dev/urandom文件。

B、使用第7行方式创建SecureRandom实例,然后javac编译,同样方式运行

strace -ff -o c.strace  java RandomTest
#会一直在这里阻塞住,没有执行结果。。。

同样方式找到文件,打开:

可以看到,SecureRandom.java底层会打开/dev/random和/dev/urandom这两个文件,但最终读取的却是8号,也就是/dev/random文件,同时read系统调用一直阻塞住了。

linux - Java SecureRandom doesn't block? How? - Information Security Stack Exchange

使用 SecureRandom 产生随机数采坑记录 - 腾讯云开发者社区-腾讯云

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/165027.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习记录660@项目管理一般知识

看了项目管理一般知识这一章的知识&#xff0c;最开始觉得这些内容&#xff0c;觉得太过于书面化&#xff0c;比如关于什么是项目管理&#xff0c;都要用一段正式定义&#xff0c;充满了国内教育的繁琐感&#xff0c;但是细细品味觉得这些定义是很有道理的&#xff0c;并不是多…

保障接口数据安全的十种方案

视频介绍 数据加密 --主要针对网络抓包 AES 对称加密 RES 非对称加密 实践中直接使用 HTTPS 对于用户个人信息及密码等敏感信息 可额外进行加密 &#xff08;如密码会进行md5加密防止撞库&#xff09; 加签验签 --甄别数据在传输过程中被篡改 通常通过哈希算法 进行验证 需…

【学习笔记】【Pytorch】十、搭建CIFAR-10 model结构和Sequential的使用

【学习笔记】【Pytorch】十、搭建CIFAR-10 model结构和Sequential的使用学习地址主要内容一、CIFAR-10 model结构介绍二、代码实现学习地址 PyTorch深度学习快速入门教程【小土堆】. 主要内容 一、CIFAR-10 model结构介绍 input : 332x32&#xff0c;3通道32x32的图片 -->…

kubernetes学习-快速上手速查手册

目录使用k3s快速搭建k8s安装k8s dashboard使用Helm部署K8S资源k8s核心命令一切推倒重来资源创建方式NamespacePodDeploymentServiceIngressConfigMapJob数据持久化搭建NFSPV和PVC使用k3s快速搭建k8s 官网地址https://www.rancher.cn/k3s/ k3s是一个轻量级的k8s&#xff0c;拥…

【SpringCloud12】Gateway服务网关

1.概述简介 1.1官网 1.Zuul 1.X 2.Gateway 1.2是什么 Cloud全家桶中有个很重要的组件就是网关&#xff0c;在1.x版本中都是采用的Zuul网关&#xff1b;但在2.x版本中&#xff0c;zuul的升级一直跳票&#xff0c;SpringCloud最后自己研发了一个网关替代Zuul&#xff0c;那就…

uboot下识别FAT32格式的U盘报错:## Valid DOS partition found ##

1、出错的现象 (1)U盘被格式成FAT32文件系统&#xff0c;在Windows和Linux系统中都可以正常识别并挂载&#xff0c;在uboot下可以正常识别但是不能挂载&#xff1b; (2)在uboot下使用usb命令可以探测到U盘&#xff0c;但是用fatls、fatinfo等命令去挂载U盘时会失败&#xff0c;…

Spring MVC+Spring+Mybatis实现支付宝支付功能

本教程详细介绍了如何使用ssm框架实现支付宝支付功能。本文章分为两大部分&#xff0c;分别是「支付宝测试环境代码测试」和「将支付宝支付整合到ssm框架」&#xff0c;详细的代码和图文解释&#xff0c;自己实践的时候一定仔细阅读相关文档&#xff0c;话不多说我们开始。 本…

永磁同步电机(PMSM)磁场定向控制(FOC)转速环PI调节器参数整定

文章目录前言一、调节器的工程设计方法二、转速环PI调节器的参数整定2.1.转速环的结构框图2.2.典型II型系统2.3.转速环PI参数整定计算公式三、转速环PI调节器设计实例3.1.永磁同步电机磁场定向的转速外环电流内环双闭环控制3.2.转速环PI参数计算3.3.仿真分析总结前言 本章节采…

【2022年度总结2023新年Flag】--2022:高考失利,我奋力奔跑的大一上;2023,朝着成为更优秀的自己迈进ing

&#x1f331;博主简介&#xff1a;是瑶瑶子啦&#xff0c;一名大一计科生&#xff0c;目前在努力学习C进阶,JavaSE。热爱写博客~正在努力成为一个厉害的开发程序媛&#xff01; ✈往期博文回顾:【C语言篇】请把这篇文章推给现在还对指针一知半解的童鞋超生动图解&#xff0c;详…

linux w5500 驱动及使用

1、驱动 驱动来源: a. 内核&#xff1a;linux内核w5500驱动&#xff0c;包含两个源文件w5100.c和w5100-spi.c /kernel/drivers/net/ethernet/wiznet/w5100.c kernel/drivers/net/ethernet/wiznet/w5100-spi.c kernel/drivers/net/ethernet/wiznet/w5100.h 可通过make menuconfi…

Django 入门学习

记录Django入门学习过程 1 基本环境 安装virtualenv pip3.9 install virtualenv 使用命令行创建 创建一个项目 virtualenv dj 进入虚拟环境 cd dj/bin source ./activate 进入成功后bash前面显示括号 安装 Django pip install django3.0.6 安装成功后之星django-…

创建者模式

1.单例模式 单例模式能够保证一个类只有一个实例&#xff0c;并且提供一个可以访问该实例的全局节点 实现步骤 类中添加一个私有静态成员变量用于保存单例实例声明一个公有静态构建方法用于获取单例实例在静态方法中实现"延迟初始化"。 该方法会在首次被调用时创建一…

【云原生kubernetes】k8s中pod使用详解

一、前言 在之前k8s组件一篇中&#xff0c;我们谈到了pod这个组件&#xff0c;了解到pod是k8s中资源管理的最小单位&#xff0c;可以说Pod是整个k8s对外提供服务的最基础的个体&#xff0c;有必要对Pod做深入的学习和探究。 二、再看k8s架构图 为了加深对k8s中pod的理解&#x…

ARM PWM 定时器与实战

一、什么是定时器&#xff08;timer&#xff09; 1、定时器是 SoC 中常见外设 (1) 定时器与计数器。计数器是用来计数的&#xff08;每隔一个固定时间会计一个数&#xff09;&#xff1b;因为计数器的计数时间周期是固定的&#xff0c;因此到了一定时间只要用计数值计数时间周…

contex-m基于IAR工程从boot阶段引导app

目录 1.修改工程 2.修改代码 Boot代码 App代码 3.修改FM33LG04x.icf 4.修改IAR工程icf配置路径 5.修改FM33LG04X.icf链接文件 6.编译工程 7.查看map文件 8.调试程序 1.修改工程 本次调试的demo为《UART0 DMA发送_串口中断示例》&#xff0c;以下修改都是基于该工程&…

初始C语言 - 函数

C语言中函数的分类&#xff1a; 1.库函数:C语言自带的函数&#xff0c;包含大量频繁使用的功能 2.自定义函数:有些库函数没有的&#xff0c;需要程序员自己设计一、库函数通过MSDN、cplusplus.com、cppreference.com这些网站&#xff0c;查看学习函数的作用和用法在使用库函数时…

portswigger靶场中目录遍历

portswigger靶场中目录遍历1.Bp靶场介绍1.1.访问靶场1.2.注意事项2.目录变量漏洞2.1.文件路径遍历2.1.1.开启靶场2.1.2.点击详情2.1.3.抓包2.1.4.修改参数2.1.5.过关2.2.用绝对路径旁路阻止遍历序列2.2.1.开启靶场2.2.2.修改参数2.2.3.过关2.3.非递归地剥离遍历序列2.3.1.开启靶…

Windows下让Qt5 QCamera响应UVC摄像头硬件按钮拍图

QCamera相机类提供了一些基本的功能&#xff0c;包括拍照和录制功能&#xff08;Windows不支持录制视频&#xff09;&#xff0c;但也有很多接口是没有封装的&#xff0c;比如有些UVC摄像头有物理按键&#xff0c;可以进行拍图等操作&#xff0c;但是QCamera没法响应硬件按钮的…

通信原理与MATLAB(十五):码间干扰和无码间干扰条件

目录1.码间干扰2.无码间干扰条件2.1 时域条件2.2 频域条件3.满足无码间干扰条件的滤波器3.1 理想低通滤波器3.2 余弦滚降滤波器4.不同滚降系数的余弦滚降滤波器时频域图4.1 代码4.2 结果图5.理想低通滤波器和升余弦滚降滤波器(α1)对比1.码间干扰 如下图所示&#xff0c;码间干…

Vue 基础之侦听器

Vue 基础之侦听器描述侦听器侦听器的创建侦听器的应用选项immediate 选项deep 选项未使用 deep 选项使用 deep 选项侦听目标对象中的单个属性描述 项目描述IDEVScode操作系统Windows 10 专业版Vue.js2.6.12 侦听器 侦听器允许开发者对数据进行监视&#xff0c;并指定数据发生…