java随机数中的陷阱

1

前言

随机数我们应该不陌生,业务中我们用它来生成验证码,或者对重复性要求不高的id,甚至我们还用它在年会上搞抽奖。今天我们来探讨一下这个东西。如果使用不当会引发一系列问题。

2

java中的随机数

我们需要在Java中随机生成一个数字。java开发中我们通常使用java.util.Random来搞,它提供了一种伪随机的生成机制。Jvm 通过传入的种子(seed)来确定生成随机数的区间,只要种子一样,获取的随机数的序列就是一致的。而且生成的结果都是可以预测的。是一种伪随机数的实现,而不是真正的随机数。来确定使用的但是有些用例直接使用可能会导致一些意想不到的问题。Random的一个普遍用法:

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

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

Math.random();

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

public static double random() {

    Random rnd = randomNumberGenerator;

    if (rnd == null) {

      // 返回一个新的Random实例

    rnd = initRNG();

   }

    return rnd.nextDouble();

    }

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

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,这意味着旧值与例外值不匹配。因为是循环内进行的操作,那么会发生自旋,直到变量与例外值匹配。这可能会导致性能不佳和线程竞争。

3

多线程下的随机数

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

4

安全的随机数

通过对Random 的一些分析我们可以知道Random事实上是伪随机,是可以推导出规律的,而且依赖种子(seed)。如果我们搞抽奖或者其他一些对随机数敏感的场景时,用Random就不合适了,容易被人钻空子。JDK提供了SecureRandom来解决这个事情。

SecureRandom是强随机数生成器,它可以产生高强度的随机数,产生高强度的随机数依赖两个重要的因素:种子和算法。算法是可以有很多的,通常如何选择种子是非常关键的因素。如Random,它的种子是System.currentTimeMillis(),所以它的随机数都是可预测的, 是弱伪随机数。强伪随机数的生成思路:收集计算机的各种信息,键盘输入时间,内存使用状态,硬盘空闲空间,IO延时,进程数量,线程数量等信息,CPU时钟,来得到一个近似随机的种子,主要是达到不可预测性。说的更通俗就是,使用加密算法生成很长的一个随机种子,让你无法猜测出种子,也就无法推导出随机序列数。

5

总结

今天我们探讨了业务中经常使用的随机数的一些机制和一些场景下的一些陷阱,希望你在使用随机数的时候能避免这种陷阱。

本文分享自微信公众号 - 码农小胖哥(Felordcn),作者:码农小胖哥

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java 8 Stream Api 中的 map和 flatMap 操作

    Java 8 提供了非常好用的 Stream API ,可以很方便的操作集合。今天我们探讨两个 Stream 中间操作 map 和 flatMap

    码农小胖哥
  • 一起了解下Java多线程基础

    使变量在多个线程间可见,没有原子性,通过加入内存屏障和 禁止重排序(as if serial)优化实现可见性。

    码农小胖哥
  • Java开发中商业计算请务必使用BigDecimal来进行计算!

    今天群里一个初级开发者问为什么测试人员测出来他写的价格计算模块有计算偏差的问题,他检查了半天也没找出问题。这里小胖哥要提醒你,商业计算请务必使用`BigDeci...

    码农小胖哥
  • 使用 SecureRandom 产生随机数采坑记录

    我们的项目工程里经常在每个函数需要用到 Random 的地方定义一下 Random 变量(如下)

    黄泽杰
  • 使用 SecureRandom 产生随机数采坑记录

    我们的项目工程里经常在每个函数需要用到 Random 的地方定义一下 Random 变量(如下)

    kunge
  • [记录点滴] 一个解决Lua 随机数生成问题的办法

    原因是LUA的random只是封装了C的rand函数,使得random函数有一定的缺陷,

    罗西的思考
  • 分享如何避免您的网站被入侵?

    在移动互联网的高速发展下,一切都开始进入了以数据和流量为王时代。那么企业想要发展就要开始改变经营模式。从而就出现了线下和线上运营模式。通过线上推广宣传扩大知名度...

    墨者安全科技
  • 社会工程学预防x1

    在阅读本文之前,要明白,你的信息或许已经被联不通移不动或者其他网站的安全漏洞泄露了,毕竟黑市上这些东西一大把一大把的。

    周俊辉
  • 网站被攻击怎么办 如何查找网站漏洞攻击源

    很多企业网站被攻击,导致网站打开跳转到别的网站,尤其一些彩票等非法网站上去,甚至有些网站被攻击的打不开,客户无法访问首页,给客户造成了很大的经济损失,很多客户找...

    网站安全专家
  • 网站设计整体策划流程

    网站设计整体策划流程 HTML5学堂:一个网站的制作过程,通常需要经历5个阶段。前端开发在整个开发过程中主要是在网站设计阶段、网站改进阶段进行参与,主要负责其中...

    HTML5学堂

扫码关注云+社区

领取腾讯云代金券