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

干掉Random这个类已经成为获取随机数的王者

  原文链接:https://mp.weixin.qq.com/s/rYoAyu4pG-2GHZYTuKvn8g前言
  最近在写一些业务代码时遇到一个需要产生随机数的场景,这时自然想到 jdk 包里的  Random 类。但出于对性能的极致追求,就考虑使用 ThreadLocalRandom  类进行优化,在查看 ThreadLocalRandom 实现的过程中,又追了下来 Unsafe  有部分代码,整个流程下来,学到了不少东西,也通过搜索和提问解决了很多疑惑,于是总结成本文。Random 的性能问题
  使用 Random 类时,为了避免重复创建的开销,我们一般将实例化好的 Random 对象设置为我们所使用服务对象的属性或静态属性,这在线程竞争不激烈的情况下没有问题,但在一个高并发的 web 服务内,使用同一个 Random 对象可能会导致线程阻塞。
  Random 的随机原理是对一个"随机种子"进行固定的算术和定位运算,得到随机结果,再使用这个结果作为下一次随机的种子。在解决线程安全问题时,Random 使用 CAS 更新下一次随机的种子,可以想到,如果多个线程同时使用这个对象,就肯定会有一些线程执行 CAS 连续失败,进而导致线程阻塞。 ThreadLocalRandom
  jdk 开发者自然考虑到了这个问题,在 concurrent 包内添加了  ThreadLocalRandom  类,第一次看到这个类名,我以为它是通过 ThreadLocal 实现的,进而想到恐怖的内存泄漏问题,但点进源码却没有 ThreadLocal 不是影子,而是存在着大量 Unsafe 相关的代码。
  我们来看一下它的核心代码:
  UNSAFE.putLong(t = Thread.currentThread(), SEED, r = UNSAFE.getLong(t, SEED) + GAMMA);
  翻译成更直观的 Java 代码就像: Thread t = Thread.currentThread(); long r = UNSAFE.getLong(t, SEED) + GAMMA; UNSAFE.putLong(t, SEED, r);
  看上去非常眼熟,像我们平常说的 Map 里 get/set 一样,以  Thread.currentThread()  获取到的当前对象里 key,以 SEED 随机种子作为 value。
  但是以对象作为 key 是可能会造成内存泄漏的啊,由于 Thread 对象可能会大量创建,在回收时不 remove Map 里的 value 时会导致 Map 越来越大,最后内存溢出来。 Unsafe功能
  不过再仔细看 ThreadLocalRandom 类似的核心代码,发现并不是简单的 Map 操作,它的 getLong() 方法需要传入两个参数,而 putLong() 方法需要三个参数,查看源码发现它们都是 native 方法,我们看不到具体的实现。两个方法签名分别是: public native long getLong(Object var1, long var2); public native void putLong(Object var1, long var2, long var4);
  虽然看不到具体实现,但我们可以查得到它们的功能,下面是两个方法的功能介绍: putLong(object, offset, value) 可以将 object 对象内存地址偏移 offset 后的位置的后四个字节设置为 value。 getLong(object, offset) 会从 object 对象内存地址偏移 offset 后的位置读取四个字节作为 long 型返回。 不安全性
  作为 Unsafe 类内的方法,它也透露着一股 "Unsafe" 的气息,具体表现就是可以直接操作内存,而不做任何安全校验,如果有问题,则会在运行时抛出  Fatal Error ,导致整个虚拟机的退出。
  在我们的常识里,get 方法是最容易抛异常的地方,比如空指针、类型转换等,但 Unsafe.getLong() 方法是个非常安全的方法,它从某个内存位置开始读取四个字节,而不管这四个字节是什么内容,总能成功转成 long 型,至于这个 long 型结果是不是跟业务匹配就是另一回事了。而 set 方法也是比较安全的,它把某个内存位置之后的四个字节覆盖成一个 long 型的值,也几乎不会出错。
  那么这两个方法"不安全"在哪呢?
  它们的不安全并不是在这两个方法执行期间报错,而是未经保护地改变内存,会引起别的方法在使用这一段内存时报错。 public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {         // Unsafe 设置了构造方法私有,getUnsafe 获取实例方法包私有,在包外只能通过反射获取         Field field = Unsafe.class.getDeclaredField("theUnsafe");         field.setAccessible(true);         Unsafe unsafe = (Unsafe) field.get(null);         // Test 类是一个随手写的测试类,只有一个 String 类型的测试类         Test test = new Test();         test.ttt = "12345";         unsafe.putLong(test, 12L, 2333L);         System.out.println(test.value);     }
  运行上面的代码会得到一个 fatal error,报错信息为 "A fatal error has been detected by the Java Runtime Environment: … Process finished with exit code 134 (interrupted by signal 6: SIGABRT)"。
  可以从报错信息中看到虚拟机因为这个 fatal error abort 退出了,原因也很简单,我没有使用 unsafe 将 Test 类 value 属性的位置设置成了 long 型值 2333,而当我使用 value 属性时,虚拟机会将这一块内存解析为 String 对象,原 String 对象对象头的结构被打乱了,解析对象失败抛出了错误,更严重的问题是报错信息中没有类名行号等信息,在复杂项目中排查这种问题真如同大海捞针。
  不过 Unsafe 的其他方法可不一定像这一对方法一样,使用他们时可能需要注意另外的安全问题,之后有遇到再说。 ThreadLocalRandom 的实现
  那么 ThreadLocalRandom 是不是安全的呢,再回过头来看一下它的实现。
  ThreadLocalRandom 的实现需要 Thread 对象的配合,在 Thread 对象内存在着一个属性  threadLocalRandomSeed ,它保存着这个线程专属的随机种子,而这个属性在 Thread 对象的 offset,是在 ThreadLocalRandom 类加载时就确定了的,具体方法是 SEED = UNSAFE.objectFieldOffset(Thread.class.getDeclaredField("threadLocalRandomSeed"));
  我们知道一个对象所占用的内存大小在类被加载后就确定了的,所以使用  Unsafe.objectFieldOffset(class, fieldName)  可以获取到某个属性在类中偏移量,而在找对了偏移量,又能确定数据类型时,使用 ThreadLocalRandom 就是很安全的。疑问
  在查找这些问题的过程中,我也产生了两个疑问点。 使用场景
  首先就是 ThreadLocalRandom 为什么非要使用 Unsafe 来修改 Thread 对象内的随机种子呢,在 Thread 对象内添加 get/set 方法不是更方便吗?
  stackOverFlow 上有人跟我同样的疑问,why is threadlocalrandom implemented so bizarrely,被采纳的答案里解释说,对 jdk 开发者来说 Unsafe 和 get/set 方法都像普通的工具,具体使用哪一个并没有一个准则。这个答案并没有说服我,于是我另开了一个问题,里面的一个评论我比较认同,大意是 ThreadLocalRandom 和 Thread 不在同一个包下,如果添加 get/set 方法的话,get/set 方法必须设置为 public,这就有违了类的封闭性原则。 内存布局
  另一个疑问是我看到 Unsafe.objectFieldOffset 可以获取到属性在对象内存的偏移量后,自己在 IDEA 里使用 main 方法试了上文中提到的 Test 类,发现 Test 类的唯一一个属性 value 相对对象内存的偏移量是 12,于是比较疑惑这 12 个字节的组成。
  我们知道,Java 对象的对象头是放在 Java 对象的内存起始处的,而一个对象的 MarkWord 在对象头的起始处,在 32 位系统中,它占用 4 个字节,而在 64 位系统中它占用 8 个字节,我使用的是 64 位系统,这毫无疑问会占用 8 个字节的偏移量。
  紧跟 MarkWord 的应该是 Test 类的类指针和数组对象的长度,数组长度是 4 字节,但 Test 类并非数组,也没有其他属性,数据长度可以排除,但在 64 位系统下指针也应该是 8 字节的啊,为什么只占用了 4 个字节呢?
  唯一的可能性是虚拟机启用了指针压缩,指针压缩只能在 64 位系统内启用,启用后指针类型只需要占用 4 个字节,但我并没有显示指定过使用指针压缩。查了一下,原来在 1.8 以后指针压缩是默认开启的,在启用时使用  -XX:-UseCompressedOops  参数后,value 的偏移量变成了 16。小结
  在写代码时还是要多注意查看依赖库的具体实现,不然可能踩到意想不到的坑,而且多看看并没有坏处,仔细研究一下还能学到更多。

女生为什么想不通男友愿意花4000块钱买一张显卡,老程来告诉你导读现在很多人配电脑趋于理性化了,但是还是有很多人为了高性能,发烧配置而花钱,如果你是单身一人或者还是家里给你的消费负责,那么发烧级别肯定是没问题的,有钱,任性这个老程没办法啊,但浅谈品牌机与组装机的差异,品牌机和组装机到底哪些地方不同导读今天下雨,老程作为一个专业的无业游民,很不幸今天睡到了9点,然后起来发现自己的显卡有点脏,于是乎就出现了水洗显卡的事情,但是现在显卡已经在晾晒过程中了,那么剩下的时间我们要干啥如何装一台性价比的电脑主机老程带你了解怎么装电脑导读作为失踪人口,总要在合适的时间更新一篇合适的文章,老程又是好几天没有更新了,说实话老程是想收集素材,给大家带来更好的阅读体验老程有点懒,临沂天太热,电脑刚换显示器,好吧这些都是建个20平方的冷库多少钱?冷库行情冷库是如何计算价格的呢?建个冷库多少钱?相信行业里的人,每每有用户咨询建库的相关事宜,都会涉及到这个话题,冷库是个性化的定制品,价格的多少与实的一些数据至关重要,当用户问价冷库拆卸时的注意事项及处理方法冷库拆装基本常识概述现今社会,随着生活的越来越好,冷库的应用也十分普及,涉及到的冷藏冷冻,保温保鲜等的发展需要,冷库鲜食品已成为人们日常生活的必需品。使用商大到公司集团,小到个体个冷库制冷系统施工及注意事项氟里昂冷库系统的工艺与制作。系统连接铜管两头要封住,不能有其它杂物进入,如发现有进入异物,要及时用高压氮气冲洗干净,直到确定没有异物在内为止,杂物的进入,如不小心进入系统,不仅造成医药冷库的特点及要求医用冷库概述医药冷库顾名思意,就是医药医院医疗医用方面的冷库,称之为医药冷库,医用冷库一般应在医院医药企业医疗单位经销医用相关的公司等。医药冷库的种类医药冷库的类型有,疫苗冷库医药冷库常见的几种故障及维修方法冷库维修分析。制冷设备长时间的工作及运行中,难免会出现一些问题及故障,影响到冷库设备的正常使用,小编为你解板下列几种常用的故障及解决方法。。电源跳闸电路自身电路断路或短路,空开自身冷库安装工程的工艺要求冷库施工要素冷库作为特种设备,业内有三分质量,七分安装之说,由此可见,冷库的施工艺和安装方法相当重要,一不小心就会影响到整体工程和用户体验,造成的后果不可估量。冷库建造前的准备工作中央空调不制热是什么原因?中央空调的功能就是制冷和制热,专门调节空气冷热的设备,一般情况下是夏天制冷,冬天制热,当然不是每台空调都能制热的,只有带制热功能的空调才能制热,按照行业习惯,制热功率要比制冷功率大冷库内部结霜过多怎么办?如何处理?冷库积霜原因冷库在长期使用中,积霜会堆积过厚时,就可能影响到制冷的效果,造成效益低下的情况发生,这种现象是怎样造成的呢,如何处理,下面我们就来讨论讨论。积霜太厚时,冷量以及冷气就会
中秋节来临,怎么做一个中秋祝福H5贺卡?中秋节即将来临,你知道怎么制作一个中秋节祝福的H5页面吗?其实要想做一个创意美观的中秋祝福H5页面并不难,找一个第三方H5制作平台工具就可以了。小编这里给大家推荐一个,乔拓云网,纯9月中秋国庆线上活动营销策划方案,裂变传播实现促销转化每年的中秋与国庆相差的时间不是很长,所以很多商家会将这两个节日连在一起,那么对于商家来说,在这小长假中会进行大肆宣传并会制作一些线上促销活动进行营销,那么就可以让更多人参与到促销活多个视频批量消音并添加背景音乐的方法如果我们想要把一些视频素材添加自己的配音解说或者背景音乐的话,需要怎么做呢?有什么方法可以批量处理吗?今天,小编要跟大家分享一下怎么一键给多个视频去除声音并添加背景音乐的方法,做视国庆线上促销营销活动怎么做?新手商家必看每到节假日,商家们总是推出各种各样的营销活动与广大用户进行互动,借此吸引更多的粉丝用户群体,提高产品销售量以及门店品牌的曝光度与知名度。如今,国庆节即将来临,商家们,你们准备好怎么婚庆行业怎么做小程序?不可错过的新手教程虽然现在微信小程序大火,甚至还有不少成功的基于小程序的营销案例,但毕竟隔行如隔山,还是有很多人不知道如何开发制作微信小程序。所以,今天小编就以婚庆行业的小程序为例,给大家讲解一下如婚姻是一艘海盗船刚刚看电视剧,里边有人提到了婚姻是一艘海盗船,剧情里只有这么简单的几句话有一艘船停靠在岸边,跑船的人忽悠你,说是艘豪华游轮,等你上船,船开了你才发现是一艘贼船,这时候你是跳下去呢?孙娄店的古建筑和碑碣牌坊墓林图片来自网络孙娄店坐落在临淄城西南六公里处,周围有九条小溪会聚在孙娄店南壑,形成一潭清水(清水潭)即氵画水之源,岸上绿树成荫,潭内泉水涌动,碧波荡漾,清澈的潭水,西经八仙桥下,缓缓微信扫描二维码抽奖详细教程做抽奖类的营销活动,加入拼图元素,可以让抽奖变得更有意思,制作H5拼图游戏你以为很难?教你一个普通人也能快速搞定的制作方法,进入乔拓云智能建站平台,可以找到很多H5页面模板,选择一批量采集新浪微博相册中的高清原图的简单方法随着新浪微博被越来越多的人所熟知,也有更多的人喜欢在微博上分享美好的事物,比如原创的摄影图,有趣的网图等等。如果单张保存,保存的图片质量不一定是高清原图的,那今天我就来和大家分享一批量保存采集洋码头商品高清主图细节图的方法及步骤做一家跨境电商的店铺并不难,但其中的门道也非常多,想要做好一家店铺,对商品的选款非常重要,所以我们需要对商品的图片下重功夫。今天小编要向小伙伴们分享如何批量采集洋码头上的高清商品原批量下载POCO相册的技巧做美工的朋友对图片素材的需求是源源不断的。POCO相册网有丰富的图片设计素材,网站内涵盖多领域图片素材,可以算是做美工的朋友的福地了。如何批量采集及下载POCO相册网的高清原图素材