范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

惊艳面试官Java中关于随机数生成8种方式的思考

  Java中生成随机数常用的有下面这8种写法:简而言之,名称带安全的未必安全,名字简洁的未必简单。  Math.random()  Random  ThreadLocalRandom  SecureRandom  UUID.randomUUID()  RandomStringUtils  RandomUtils  RandomUtil
  Math.random()
  Talk is cheap, show me the code. 先上源码:  public static double random() {     return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble(); }private static final class RandomNumberGeneratorHolder {     static final Random randomNumberGenerator = new Random(); }
  从源码可以看出:Math.random本质上用的就是new Random。而且人家给了一个最佳实践:使用的时候不用每次就new一个对象,直接做成一个静态类就可以了。
  Random
  Random是伪随机数类,采用线性同余算法产生的。伪随机数是1946年冯诺依曼提出来的。他在参加一个氢弹的制造项目,需要大量的随机数模拟核聚变、核裂变。因为当时设备没办法去存储大量的随机数,所以就想到了这个办法。伪随机数(或称伪乱数)这个称呼是因为真随机数都不是计算出来的。这里是使用一个确定性的算法计算出来的似乎是随机的数序,因此伪随机数实际上并不随机。从更宏大的视角看来,是非常有规律的,就像我在《大话高可用》里提到的炊烟。
  下面来说说这个线性同余算法,大家不用担心。我比大家还对算法一窍不通。但是我有人类的常识,这个常识是什么呢?既然是计算得到的,并且人类看起来没有规律,那一定有一个外部因子(种子),种子是一个变量。顺着这个思路看源码:   /**  * Creates a new random number generator. This constructor sets  * the seed of the random number generator to a value very likely  * to be distinct from any other invocation of this constructor.  */ public Random() {     this(seedUniquifier() ^ System.nanoTime()); }
  没想到在构造函数里就给出了答案,这个因子就是系统时间,精确到纳秒。这里为了方便描述,我们举例想要一个int类型的随机数(实际它可以产生其他数值类型的随机数)。来看看源码:  public int nextInt() {     return next(32); }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)); }
  解释一下:调用nextInt默认会生成32个比特位的数字。调用next方法,会有oldseed和nextseed。计算出nextseed会使CAS,将nextseed替换现有oldseed。nextseed的计算方法是oldseed做一些运算,运算的其他数值都是常量。最终返回nextseed值。
  因为使用了AtomicLong、自旋+CAS(可以参考《系统梳理一下锁》),所以Random生成随机数是线程安全的。new一个全局变量,下次不用新建,Math.random的调用方法是合理的。
  通过这个源码大胆猜测一下:如果两个不同的进程或者线程,在纳秒级相同的时间同时调用new Random,这时候nextInt的返回值是相同的!下次调用nextInt的值也相同!想要验证的话,可以下载java源码,在new Random的地方稍作修改,
  System.nanoTime()改成固定值,这里我就不验证了。
  ThreadLocalRandom
  上面提到Random使用了线程安全的算法:AtomicLong、自旋+CAS。这在保证线程安全的同时造成了很大的性能开销。ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争。
  它不是直接用new实例化,而是第一次使用时初始化其静态方法current()。直接调用静态方法,可以每个线程实例化一个。有朋友测试过,100个线程的情况下,3分钟,Math.random可以跑几百次。而
  ThreadLocalRandom.current().nextInt()可以跑十几万次!
  来看一下它是怎么做到的:  public static ThreadLocalRandom current() {     if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)         localInit();     return instance; } static final void localInit() {     int p = probeGenerator.addAndGet(PROBE_INCREMENT);     int probe = (p == 0) ? 1 : p; // skip 0     long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));     Thread t = Thread.currentThread();     UNSAFE.putLong(t, SEED, seed);     UNSAFE.putInt(t, PROBE, probe); }
  简而言之,Random是通过系统时间这个外部因子,而ThreadLocalRandom都是通过UNSAFE调用本地方法拿到线程本身的一些变量作为外部因子。所有的参数绑定在线程本身,和其他线程没有竞争,所以可以不加锁就保证线程安全。
  public int nextInt() {     return mix32(nextSeed()); }private static int mix32(long z) {     z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;     return (int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32); }final long nextSeed() {     Thread t; long r; // read and update per-thread seed     UNSAFE.putLong(t = Thread.currentThread(), SEED,                    r = UNSAFE.getLong(t, SEED) + GAMMA);     return r; }
  生成随机数计算的时候也都是调用线程内的变量,不加锁。大家可以举一反三一下,回忆一下ThreadLocal相关知识。
  SecureRandom
  在安全应用场景,随机数应该使用安全的随机数。密码学意义上的安全随机数,要求必须保证其不可预测性。
  由于 Random 是采用时间作为随机数种子,如果黑客知道随机数产生的时间,那就能重现随机数。而 SecureRandom 属于强随机数,一般不单独采用时间作为随机数种子,除了系统时间,还会采用临时文件夹中大小、某个线程从休眠到被唤醒所耗的时间等等一系列无法重现的值作为随机数种子。
  因为SecureRandom 采用了很多外部参数,会产生熵源不足时阻塞问题。在我做过的项目中,因为有的业务凌晨没有流量,这个问题实际发生过。建议有明显低谷的业务,低谷时低于1TPS时不要用。
  像SecureRandom这种强随机在很多场景下效果不一定好。有一个小故事说itunes之前播歌的时候采用的是真随机。有些用户总是抱怨说你这是真随机吗?怎么来来回回给我播放那几首歌。苹果公司有苦说不出,于是把算法改成伪随机:洗牌算法,先把所有的歌打乱顺序,然后按顺序播放。改完之后用户纷纷点赞。
  适合使用SecureRandom的场景,比如要给每个资源生成一个url,为了防止url规律被别人攻破,使用爬虫爬取到自己的资源可以用这个。
  UUID.randomUUID()
  看下面的代码就知道UUID.randomUUID()是基于SecureRandom  public static UUID randomUUID() {     SecureRandom ng = Holder.numberGenerator;     byte[] randomBytes = new byte[16];     ng.nextBytes(randomBytes);     randomBytes[6]  &= 0x0f;  /* clear version        */     randomBytes[6]  |= 0x40;  /* set to version 4     */     randomBytes[8]  &= 0x3f;  /* clear variant        */     randomBytes[8]  |= 0x80;  /* set to IETF variant  */     return new UUID(randomBytes); }
  有的朋友说不对呀,网上告诉我UUID是时间戳+时钟序列+MAC地址。注意上面代码注释有个set to version 4。
  从UUID的类注释里就可以看到java 8提到4个版本:  The version field holds a value that describes the type of this {@code * UUID}.  There are four different basic types of UUIDs: time-based, DCE * security, name-based, and randomly generated UUIDs.  These types have a * version value of 1, 2, 3 and 4, respectively.
  翻译一下:
  版本1 - 基于时间
  版本2 - 基于DCE sercrity
  版本3 - 基于名字(MD5)
  版本4 - 基于随机数
  记住啦:java默认UUID是基于随机数的!
  证明一下:如果是基于时间戳的,生成的UUID前几位应该相同  @Test public void test() {     for (int i = 0; i < 3; i++) {         System.out.println(UUID.randomUUID());     } }
  结果:
  a0858771-c903-4061-ba6a-53efc372d8a0
  17ebdc58-db6c-452f-9b21-b4f54d3958a5
  116d6a4f-e50a-4001-b94c-61596da3a75d
  事实胜于记忆中的知识,它有可能是错的!
  java UUID也支持版本3:   @Test public void test() {     for (int i = 0; i < 3; i++) {         System.out.println(UUID.nameUUIDFromBytes("编程一生".getBytes()));     } }
  运行结果:
  743f24b2-0914-363b-ace1-f4da750dccad
  743f24b2-0914-363b-ace1-f4da750dccad
  743f24b2-0914-363b-ace1-f4da750dccad
  这个在你的机器上执行也是这个结果,就是MD5了一下。
  有人说版本1、版本2怎么用,好像还听说过版本5。可以用linux命令
  uuid -n 3 -v1
  运行结果:
  5b01cea2-9561-11e9-965b-a3d050dd0f23
  5b01cf60-9561-11e9-965c-1b66505f5845
  5b01d118-9561-11e9-965d-97354eb9e914
  linux命令版本可指定!
  RandomStringUtils、RandomUtils和RandomUtil
  这两个不是Java原生的方法,apache commons下有这两个工具类,本质上还是Random。跟进代码去看可以发现:RandomUtils用的是JVMRandom。这个只不过是封装了一个静态的Random,所有使用RandomUtils类的,全局只用一个,减少了新建对象成本。
  如果你问我一般情况下建议用哪个,我选ThreadLocalRandom。那这么好的方法是不是应该也有对应的封装工具类呢?有的,hultool就搞了一个RandomUtil,
  RandomUtil.getRandom()返回的就是ThreadLocalRandom。
  RandomUtil.getSecureRandom()返回的是SecureRandom,提供多种选择。
  总结
  一张图表示他们之间的关系:
  在《CURD系统怎么做出技术含量--怎样引导面试》里我提到可以利用工作中总结的技巧惊艳面试官,如果能把一个问题系统性讲明白,甚至让面试官意识到之前的知识竟然是错的,也一定能让面试官眼前一亮。
  原文链接:https://mp.weixin.qq.com/s/9ReYiLCyvJKPXK07fXmrvg

越南军队遭到双重打击,副司令当场殉职,46000人被迫紧急逃离现如今,各国仍旧被疫情危机笼罩着,对世界格局也产生了极大的影响。美国作为世界范围内唯一拥有霸主地位的国家,在这样的关键时刻,非但没有起到良好的带头作用,还频频挑衅别国。而随着亚洲强阿塔主动对华示好,高层会见我驻阿大使,竟然向中方提出两大诉求塔利班组织在占领喀布尔之后,宣布战争正式结束。但阿富汗的混乱远没有结束,塔利班虽然宣布组建新的国家,但尚未控制整个阿富汗。部分反抗势力还在进行着抗争。驻阿富汗大使馆还在正常运转当中有一次,冯小刚故意问周星驰你觉得莫文蔚算得上是一个美女吗有一次,冯小刚故意问周星驰你觉得莫文蔚算得上是一个美女吗?问完之后冯小刚一脸坏笑地看着他,不想周星驰直接起身就要往外走,一直到大门口,周星驰才回头一边一边对冯小刚说有这么美。意思是完美运行centos7上的PHP服务器配置首先,安装好NGINX服务器(安装方法请参加我发布的centos7搭建nginx网站服务器一次性通过文章)。现在开始配置PHP服务器一首先安装PHP服务1安装php的lib库yum修复桌面上WORDExcel图标显示不正常在平时的使用中,有时安装或者卸载完程序的时候,你会发现你的桌面上WORDEXCEL的图标跟平时不一样,WORDEXCEL都能正常打开,就是这个图标看起来很别扭,感觉很不顺眼,今天就荣耀是否会延续之前在华为的辉煌没有了华为二字作为前缀的荣耀,已经开始在独行路上加速前进,此时,亟需一款自证实力的产品。1三个系列,一个子品牌荣耀去年巅峰时期的市场份额达到了16。7,后来经历一段艰难的时期,最低续航不到500KM,依旧热销抢手?电动汽车的真正焦虑在哪?文岸青回顾如今的新能源汽车销售市场,特斯拉绝对占有着不可忽视的地位,哪怕它这一年来黑料满天飞,却依旧销量火爆。在此前的文章中,我们提到过这一行业情况,除了其自身产品力过关之外,它还各大品牌加大布局AR眼镜,苹果依旧继续领先AR成为万物互联时代的新窗口。智能手表的屏幕TWS可以作为手机部分功能的替代品,但智能手表的尺寸有限,屏幕太小,使用体验大打折扣。智能眼镜则很好地弥补了这一缺陷,它可以通过投射将虚32位已死,对安卓和苹果意味着什么?这是一个分水岭。没有32位操作系统,也没有32位应用程序。这是一个分水岭。ARM已经宣布,从2023年开始,其所有新的智能手机CPU内核都将只有64位,并且不会有32位兼容模式。苹外媒苹果正研发新的AppleWatch机型据外电报道,苹果公司(AppleInc。)正在研发一款新的AppleWatch机型,该机型将采用新的显示器新的温度和血糖传感器更快的处理器以及改进的无线连接。近日,有消息称,新款A盘点微软正在使用的编程语言对于微软这个体量的公司,他们使用的编程语言绝不可能仅仅是一种,他一定会使用多种编程语言来进行相关业务的开发,毕竟每种语言都有最适合它的领域。java是的,你没有看错,微软公司内容也
华为MateXRollable抽拉式卷屏曝光,展开11寸大屏据悉,荷兰科技博客LetsGoDigital曾多次爆料华为正在酝酿的多种智能机设计,例如一款采用曲面屏隐藏式前置自拍摄像头的概念设备。此外,该公司似乎也在开发一款采用可伸缩设计的M8月31日华为品牌日终极攻略来袭P50M40买买买手机中国新闻8月30日,手机中国了解到,华为手机和苏宁将会在明日开启华为品牌日活动。在活动当日,华为P50系列华为Mate40系列以及华为WATCH3系列稀缺货源限时抢购。如果最近鸿蒙始于智慧屏曾被质疑,而如今谷歌FuchsiaOS也首发家庭设备了P50发布之际,鸿蒙官宣用户已经突破4000万。其实,令更多国人惊异的那一部分是鸿蒙作为一个对极限压制的快速回应,短短两年时间,就已到今天的规模。至于连接万物分布式技术这些技术内涵80后江苏小伙的比特币之路,赚了180亿还登上福布斯封面赚了180亿!币圈玩家登上福布斯封面,江苏小伙的比特币之路为什么如此看好数字货币?许多人认为比特币是自互联网以来最伟大的技术创新,他们认为第一比特币可以永远改变个人和公司做生意的方阿里将解雇10名外泄性侵指控内网文章的员工知情人士透露,阿里巴巴将对外泄露控诉前公司经理性侵内网文章的10名员工解雇。据彭博社报道,因涉及讨论内部事务要求匿名的知情人士透露,阿里巴巴上周对内宣布,针对周姓员工在内网发帖控诉耳梦播报vivo宣布将从9月13号开始实行双休工作制此前vivo发布群内通知,称公司计划取消大小周工作制,改为每周5天工作制。员工可根据工作需要申请周六日加班关注度最高的永远都是顶级的互联网大厂,他们可以自由实行双休。只能说拍手叫好造车鄙视链,滴滴垫底今日话题提到汽车圈鄙视链,几乎无处不在,就连刚刚兴起新能源车也有,而滴滴则意外垫底,这到底是为什么1汽车圈鄙视链?据公开资料显示,劳斯莱斯和宾利都是世界殿堂级豪华车,但是开大劳的鄙过来人总结买手机卡的经验,这些踩过的坑,得注意了现在的手机毫无例外基本都是已经支持双卡双待的,如今身边的许多人都是一张主卡和一张流量卡先配套,我本人也不例外,办理一张手机卡是一件大事,今天就来聊聊怎么防止被坑。第一,在办理套餐要iQOO8Pro一手用机体验分享!真的有那么绝吗?前阵子,手机市场的热闹场面想必大家都还记得,华为小米等等一众大品牌集体推出新机,牢牢圈了一波关注度,尤其是iQOO8Pro的正式发布更是让不少粉丝大呼等到了。那么,这款传说中的宝藏国内5G市场发展如何?华为被迫启动新计划,余承东看到希望了本文导读国内5G市场发展如何,华为下一款旗舰机Mate50再传新消息,正常发布的华为旗舰机不得不启动新计划,华为手机被逼出的绝地求生,可能也是唯一的选择,余承东看到国产化手机仅剩一天玑SOC实力见证机型?今天给大家带来的机型是来自VIVO旗下的子品牌IQOO旗下的IQOOZ1,虽然该款机型距离现在已经发布快一年多了,但是该款机型的实力却依然很给力。首先来谈谈该机的性能部分,该款机型