浅谈java随机数的陷阱

 更新时间:2019年09月09日 15:02:07   转载 作者:码农小胖哥  
这篇文章主要介绍了浅谈java随机数的陷阱,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

随机数我们应该不陌生,业务中我们用它来生成验证码,或者对重复性要求不高的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://segmentfault.com/a/1190000020332761

相关文章

  • springboot~ObjectMapper~dto到entity的自动赋值

    springboot~ObjectMapper~dto到entity的自动赋值

    这篇文章主要介绍了springboot~ObjectMapper~dto到entity的自动赋值,本文分三种情况给大家介绍,需要的朋友可以参考下
    2018-08-08
  • Java中实现线程的超时中断方法实例

    Java中实现线程的超时中断方法实例

    之前在使用Java实现熔断降级组件的时候,需要实现接口请求的超时中断,通过查找相关资料了解了相关的方法,下面这篇文章主要给大家介绍了关于Java中实现线程的超时中断的相关资料,需要的朋友可以参考下
    2018-06-06
  • Java多线程atomic包介绍及使用方法

    Java多线程atomic包介绍及使用方法

    这篇文章主要介绍了Java多线程atomic包介绍及使用方法,涉及原子更新基本类型介绍及代码示例,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • Apache Commons fileUpload实现文件上传之一

    Apache Commons fileUpload实现文件上传之一

    这篇文章主要介绍了Apache Commons fileUpload实现文件上传之一的相关资料,需要的朋友可以参考下
    2016-03-03
  • cmd中javac和java使用及注意事项详解

    cmd中javac和java使用及注意事项详解

    这篇文章主要介绍了cmd中javac和java使用及注意事项详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • Java 线程死锁的问题解决办法

    Java 线程死锁的问题解决办法

    这篇文章主要介绍了 Java 线程死锁的问题解决办法的相关资料,希望通过本大家能帮助到大家,遇到类似问题能够解决,需要的朋友可以参考下
    2017-09-09
  • 智能手表开发API接口

    智能手表开发API接口

    这篇文章主要介绍了智能手表开发API接口,使用图灵机器人平台接口实现天气预报,非常简单实用,这里推荐给大家。
    2015-03-03
  • 详解SpringBoot 添加对JSP的支持(附常见坑点)

    详解SpringBoot 添加对JSP的支持(附常见坑点)

    这篇文章主要介绍了详解SpringBoot 添加对JSP的支持(附常见坑点),非常具有实用价值,需要的朋友可以参考下
    2017-10-10
  • 举例详解Java中的访问权限修饰符

    举例详解Java中的访问权限修饰符

    这篇文章主要介绍了举例详解Java中的访问权限修饰符,是Java入门学习中的基础知识,需要的朋友可以参考下
    2015-08-08
  • Java线程池的应用实例分析

    Java线程池的应用实例分析

    这篇文章主要介绍了Java线程池的应用,结合具体实例形式分析了java线程池的斐波那契数列计算与模拟工人做工等应用的操作技巧,需要的朋友可以参考下
    2019-10-10

最新评论