当前位置:   article > 正文

java原子类生成随机数,伪随机数(示例代码)

为什么nextint是伪随机

笔者最近在练习Mysql语句优化,奈何年少不懂,找不到百万级别的测试数据,只好用java随机生成数据凑合用一下,所以写下此篇博客,经测试生成500万条数据后台用了9秒,完全可以接受

1. Random

random伪随机数类在 java.util 包下,是最常用的随机数生成器,其使用线性同余公式来生成随机数,所以才说是伪随机。该类的实例是线程安全的,多线程并发使用可能会遇到争用问题,这时可用 ThreadLocalRandom 来解决这个问题,此外还有 SecureRandom 、SplittableRandom 随机生成器,这里就不扩展说明了

2. 构造方法与常用方法

类型

名字

解释

Random()

默认构造函数

Random(long seed)

有参构造,用种子创建伪随机生成器

int

nextInt

返回生成器中生成表序列中的下一个伪随机数

int

nextInt(int n)

返回均匀分布于区间 [0,n)的伪随机数

double

nextDouble

返回下一个伪随机数 [0.0,1.0)

3. 具体分析

先看无参构造,直接上源码

// 无参构造也是调用有参构造的,那么放出有参构造,再看里面具体内容

public Random() {

this(seedUniquifier() ^ System.nanoTime());

}

// 有参构造接收长整型种子参数

public Random(long seed) {

// 判断是否本类

if (getClass() == Random.class)

// 可以看出长整型种子是Atomic原子型的,即线程安全

// initialScramble() 是seed与两个具体数值运算,这里不给出了

this.seed = new AtomicLong(initialScramble(seed));

else {

// subclass might have overriden setSeed

// 翻译:子类可能重写setSeed方法

this.seed = new AtomicLong();

setSeed(seed);

}

}

// 再回无参构造内部

// 其中 ^ System.nanoTime() 表示与系统纳秒异或运算,也就是说随机数依赖于时间

this(seedUniquifier() ^ System.nanoTime());

private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L);

private static long seedUniquifier() {

// L'Ecuyer, "Tables of Linear Congruential Generators of

// Different Sizes and Good Lattice Structure", 1999

// 翻译:不同大小结构良好的线性同余生成元表,

for (;;) {

long current = seedUniquifier.get();

long next = current * 181783497276652981L;

// 用到了CAS轻量锁

if (seedUniquifier.compareAndSet(current, next))

return next;

}

}

再看nextInt方法,有参的方法用逻辑运算把范围指定,这里就不介绍了

public int nextInt() {

return next(32);

}

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)); // 可能这些位运算就是线性同余把

}

简单使用

Random r1 = new Random();

Random r2 = new Random();

Random r3 = new Random();

Random r4 = new Random(1000);

Random r5 = new Random(1000);

System.out.println(r1.nextInt());

System.out.println(r2.nextInt());

System.out.println(r3.nextInt(100));

System.out.println(r4.nextInt());

System.out.println(r5.nextInt());

491030142

2021835847

49

-1244746321

-1244746321

从结果和源码可以看出:

这里补充一下seed是final类型,线程更安全

给定seed之后,伪随机数的序列是确定的

而没有给seed因为依赖于变化的时间,所以每次的序列是不确定的

常用 new Random.nextInt(int n)来生成伪随机数

4. Math.random

我们最常用还是这个函数,静态调用方便简单

// 底层还是用了Random类

public static double random() {

return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();

}

// 新建一个依赖时间的随机数生成器

private static final class RandomNumberGeneratorHolder {

static final Random randomNumberGenerator = new Random();

}

// 位运算加强随机

public double nextDouble() {

return (((long)(next(26)) << 27) + next(27)) * DOUBLE_UNIT;

}

从源码可以看出:

这个类方便我们使用伪随机数,每次调用就新建一个Random类

也知道区间为 [0.0,1.0)

生成给定范围的伪随机数

// 给定范围

int min = 10;

int max = 15;

// 生成伪随机小数

double num = Math.random();

// 范围逻辑运算,想一下很简单的

int rs = (int)(num * (max - min + 1) + min);

System.out.println(rs); // 需要整数的位数

5. 这里贴一下生成测试数据中密码的逻辑

// 密码字符范围

String range = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&*()_+[];',.<>?:{}|";

// 生成100个伪随机密码

for(int i = 0; i < 100; i++){

// 字符串

StringBuffer bf = new StringBuffer();

// 密码长度8~20

int len = (int)(Math.random() * (20 - 8 + 1) + 8);

for(int j = 0; j < len; j++){

int index = new Random().nextInt(range.length());

bf.append(range.charAt(index));

}

System.out.println(bf.toString());

}

_ho1O@

|4z$1sDIDRt_o{PR

H_}z;A9;K74amjb2r

O;*89#b!|4w|;z?~

s+EmeTCdpJ9?W8,lNNl|

o2#P9R@,hFT

{+})BECM.Jf|&

// 完全看不懂,还可以加上MD5加密

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

闽ICP备14008679号