java随机数的使用(在Java中使用随机数的正确姿势是什么?)

/ 0评 / 0

我们应该熟悉随机数。在业务上,我们用它们来生成对重复性要求不高的验证码或id,甚至在年会上用它们来抽奖。2021-09-28 我们要讨论这个事情。如果使用不当,会引起一系列问题。

java中的随机数

我们需要Baxter在Java中随机生成一个数字。在java开发中,我们通常使用java.util.Random,它提供了一种伪随机生成机制。Jvm通过传入的种子确定生成随机数的间隔。只要种子相同,得到的随机数序列是一致的。产生的结果都是可以预测的。它是伪随机数的实现,而不是实随机数。确定最佳网络的使用,但有些用例可能会导致一些意想不到的问题。随机的一个常见用法:

// Random 实例
Random random = new Random();
//调用 nextInt() 方法 此外还有nextDouble(), nextBoolean(), nextFloat(), ...
random.nextInt();

或者,我们可以使用java中的数学计算类:

Math.random();

数学类只包含一个随机实例来生成随机数:

public static double random() {
Random rnd = randomNumberGenerator;
if (rnd == null) {
// 返回一个新的Random实例
rnd = initRNG();

return rnd.nextDouble();
}

java.util.Random的用法是线程安全的。但是,在不同线程上并发使用相同的Random实例可能会导致争用,从而导致性能下降。原因是使用所谓的种子来生成随机数。Seed是一个简单的数字,它为生成新的随机数提供了基础。让我们看看随机中的下一个(整数位)方法:

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));}

首先,旧种子和新种子存储在两个辅助变量上。在这一点上,创造新种子的原则并不重要。若要保存新种子,请使用compareAndSet()方法用下一个新种子替换旧种子,但这只会在旧种子对应于当前设置的种子时触发。如果此时的值由并发线程操作,则该方法返回false,这意味着旧值与异常值不匹配。因为这是一个在循环中执行的操作,所以旋转将会发生,直到变量与异常值匹配。这可能会导致性能下降和线程竞争。

多线程下的随机数

如果更多的线程使用相同随机数的实例主动生成新的随机数,出现上述情况的概率就越高。对于生成许多(非常多)随机数的程序,不建议使用这种方法。在这种情况下,您应该使用ThreadLocalRandom,它是在1.7版中添加到Java中的。ThreadLocalRandom扩展了Random,并添加了一些选项来限制其在相应线程实例中的使用。因此,ThreadLocalRandom的实例保存在对应线程的内部映射中,通过调用current()返回对应的Random。使用方法如下:

ThreadLocalRandom.current().nextInt()

安全随机数

通过对Random的一些分析,我们可以知道Random实际上是伪随机的,可以有规律地推导出来,并且依赖于种子。如果我们搞彩票或者其他对随机数敏感的场景,用随机数是不合适的,容易被人利用。JDK提供了安全的世界来解决这个问题。

SecureRandom是一个强大的随机数生成器,可以(www.isoyu.com原创版权)生成高强度的随机数。高强度随机数的产生取决于两个重要因素:种子和算法。算法有很多,如何选择种子通常是一个关键因素。Random的种子是System.currentTimeMillis(),所以它的随机数是可预测的,是弱伪随机的。强伪随机数的生成思路:收集各种计算机信息,键盘输入时间、内存使用状态、硬盘可用空间、IO延迟、进程数、线程数等信息,CPU时钟,得到一个近似的随机种子,主要实现不可预测性。更笼统地说,使用加密算法生成一个长的随机种子,使你无法猜测种子并推导出随机序列号。

摘要

2021-09-28 我们讨论了一些业务中经常用到的随机数的机制,以及一些场景中的一些陷阱,希望大家在使用随机数的时候能够避免这样的陷阱。