Java随机数的坑你踩过吗

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

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

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(int bits)方法:

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

多线程下的随机数

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

ThreadLocalRandom.current().nextInt()

安全的随机数

通过对Random的一些分析我们可以知道Random事实上是伪随机,是可以推导出规律的,而且依赖种子(seed)。如果我们搞抽奖或者其他一些对随机数敏感的场景时,用Random就不合适了,容易被人钻空子。JDK提供了SecureRandom来解决这个事情。 SecureRandom是强随机数生成器,它可以产生高强度的随机数,产生高强度的随机数依赖两个重要的因素:种子和算法。算法是可以有很多的,通常如何选择种子是非常关键的因素。 Random的种子是System.currentTimeMillis(),所以它的随机数都是可预测的, 是弱伪随机数。强伪随机数的生成思路:收集计算机的各种信息,键盘输入时间,内存使用状态,硬盘空闲空间,IO延时,进程数量,线程数量等信息,CPU时钟,来得到一个近似随机的种子,主要是达到不可预测性。说的更通俗就是,使用加密算法生成很长的一个随机种子,让你无法猜测出种子,也就无法推导出随机序列数。

总结

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

原创文章,作者:晴川运维,如若转载,请注明出处:https://baike.qcidc.com/14380.html

(0)
晴川运维晴川运维
上一篇 2025年10月5日
下一篇 2025年10月5日

相关推荐

  • Linux重定向和管道符注意事项

    简而言之,输入重定向就是把文件导入到命令中, 输出重定向就是把原本要输出到屏幕的数据信息写入到指定文件中。在日常的使用中,我们使用输出重定向频率更高,所以又将输出重定向分为了标准输…

    Linux系统 2025年6月24日
  • 详解MySQL 慢查询

    慢查询日志用来记录在 MySQL 中执行时间超过指定时间的查询语句。通过慢查询日志,可以查找出哪些查询语句的执行效率低,以便进行优化,下面为大家详细讲解一下 MySQL 慢查询。 …

    Linux系统 2025年9月17日
  • Mongodb复制集的安装和部署

    Mongodb复制集由一组Mongod实例(进程)组成,包含一个Primary节点和多个Secondary节点,Mongodb Driver(客户端)的所有数据都写入Primary…

    Linux系统 2025年6月22日
  • 通过GIt备份Linux文件

    我们使用 Linux 服务器时,有时候需要备份配置文件。传统的备份方法是复制文件,改名,并在文件结尾插入一些字符。但,如果我们使用 Git ,我们可以很轻松的管理配置文件的备份。 …

    Linux系统 2025年10月11日
  • Linux中通过osquery监控文件的完整性

    osquery 是 SQL 驱动的分析和监控操作系统的工具,是操作系统分析框架,支持 OS X 和 Linux 系统。osquery 能帮助监控和分析低水平的操作系统,提供更直观的…

    Linux系统 2025年6月8日
  • 学生党如何保护自己的网络安全

    互联网的兴起给人们的教育带来了巨大的好处。但是,这也使人们成为黑客和敏感信息盗窃的目标,这就是为什么每个学生都必须在某个时候了解在线网络安全的原因。 如今,几乎每个学生都拥有一台智…

    Linux系统 2025年6月25日
  • 如何使用 Ansible 安装软件

    使用 Ansible 剧本自动安装和更新设备上的软件。 Ansible 是系统管理员和开发人员用来保持计算机系统处于最佳状态的一种流行的自动化工具。与可扩展框架一样,Ansible…

    Linux系统 2025年6月8日
  • CentOS中使用RPM包安装redis具体方法

    Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类keyvalue存储的不足,在部 分场合可以对关系数据库起到很好的补充作…

    Linux系统 2025年6月8日
  • Linux Tee 命令使用实例

    Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。在执行Linux命令时,我们既想把输出保存到文件中,又想在屏幕上看到输出内容,那么可以用到tee…

    Linux系统 2025年6月8日
  • mysql实现高可用架构之MHA

    MHA(Master HA)是一款开源的 MySQL 的高可用程序,它为 MySQL 主从复制架构提供了 automating master failover 功能。MHA 在监控…

    Linux系统 2025年6月10日

发表回复

登录后才能评论