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日

相关推荐

  • CentOS8和RHEL8配置EPEL仓库具体方法

    CentOS 源包含的大多数的库都是比较旧的。并且,很多流行的库也不存在。EPEL 在其基础上不仅全,而且还够新,本篇文章重点为大家讲解一下CentOS8和RHEL8配置EPEL仓…

    Linux系统 2025年6月4日
  • Linux下安装LDAP

    LDAP是一个得到关于人或者资源的集中、静态数据的快速方式。LDAP是一个用来发布目录信息到许多不同资源的协议。通常它都作为一个集中的地址被使用,不过根据组织者的需要,它可以做得更…

    Linux系统 2025年6月8日
  • 面向系统管理员的网络管理指南

    一个使管理服务器和网络更轻松的 linux 工具和命令的参考列表。 如果你是一位系统管理员,那么你的日常工作应该包括管理服务器和数据中心的网络。以下的 Linux 实用工具和命令 …

    Linux系统 2025年6月8日
  • Elasticsearch中Head插件具体使用方法

    elasticsearch-head是一个elasticsearch的集群管理工具,它是完全由html5编写的独立网页程序,你可以通过插件把它集成到es,下面为大家分享一下Elas…

    Linux系统 2025年10月27日
  • tomcat 性能优化

    tomcat默认参数是为开发环境制定,而非适合生产环境,尤其是内存和线程的配置,默认都很低,容易成为性能瓶颈,本篇文章重点为大家讲解一下tomcat 性能优化。 Tomcat连接器…

    Linux系统 2025年10月6日
  • 我最喜欢用的 5 个 Ansible 模块

    了解如何通过这些 Ansible 模块实现几乎任何事情。 在我成长的时候,我爷爷在他的花园里有一个棚子。他经常会花几个小时在那里制作和修复东西。这是在我们有互联网之前的事情,所以我…

    Linux系统 2025年9月23日
  • 在 Kubernetes 上部署一个深度学习模型

    了解如何使用 Kubermatic Kubernetes 平台来部署、扩展与管理图像识别预测的深度学习模型。 随着企业增加了对人工智能(AI)、机器学习(ML)与深度学习(DL)的…

    Linux系统 2025年6月8日
  • Linux下安装与配置APF防火墙

    APF是一款Linux下的iptables防火墙,由R-fx Networks开发维护,实现代码基本为SHELL。个人觉得这是Linux下最易用,且强大的防火墙脚本。类似级别的还有…

    Linux系统 2025年9月19日
  • SSH连接调试小技巧

    本篇文章重点为大家讲解一下SSH连接调试小技巧,有需要的小伙伴可以参考一下。 问题一:SSH服务没有运行 SSH连接错误的常见原因是服务未在远程服务器上运行。这可能是由于服务意外关…

    Linux系统 2025年6月9日
  • 使用selenium实现cookies免密登录

    一直想用selenium实现个小功能,比如发微博之类的,但是有的网站在登录会有验证码,没想到太好的方法解决,于是想到利用cookies来登录网站。 获取一个可用的cookies 获…

    Linux系统 2025年6月13日

发表回复

登录后才能评论