安庆大理运城常德铜陵江西
投稿投诉
江西南阳
嘉兴昆明
铜陵滨州
广东西昌
常德梅州
兰州阳江
运城金华
广西萍乡
大理重庆
诸暨泉州
安庆南充
武汉辽宁

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

5月1日 海岸线投稿
  原文链接:https:mp。weixin。qq。comsrYoAyu4pG2GHZYTuKvn8g前言
  最近在写一些业务代码时遇到一个需要产生随机数的场景,这时自然想到jdk包里的Random类。但出于对性能的极致追求,就考虑使用ThreadLocalRandom类进行优化,在查看ThreadLocalRandom实现的过程中,又追了下来Unsafe有部分代码,整个流程下来,学到了不少东西,也通过搜索和提问解决了很多疑惑,于是总结成本文。Random的性能问题
  使用Random类时,为了避免重复创建的开销,我们一般将实例化好的Random对象设置为我们所使用服务对象的属性或静态属性,这在线程竞争不激烈的情况下没有问题,但在一个高并发的web服务内,使用同一个Random对象可能会导致线程阻塞。
  Random的随机原理是对一个随机种子进行固定的算术和定位运算,得到随机结果,再使用这个结果作为下一次随机的种子。在解决线程安全问题时,Random使用CAS更新下一次随机的种子,可以想到,如果多个线程同时使用这个对象,就肯定会有一些线程执行CAS连续失败,进而导致线程阻塞。ThreadLocalRandom
  jdk开发者自然考虑到了这个问题,在concurrent包内添加了ThreadLocalRandom类,第一次看到这个类名,我以为它是通过ThreadLocal实现的,进而想到恐怖的内存泄漏问题,但点进源码却没有ThreadLocal不是影子,而是存在着大量Unsafe相关的代码。
  我们来看一下它的核心代码:
  UNSAFE。putLong(tThread。currentThread(),SEED,rUNSAFE。getLong(t,SEED)GAMMA);
  翻译成更直观的Java代码就像:ThreadtThread。currentThread();longrUNSAFE。getLong(t,SEED)GAMMA;UNSAFE。putLong(t,SEED,r);
  看上去非常眼熟,像我们平常说的Map里getset一样,以Thread。currentThread()获取到的当前对象里key,以SEED随机种子作为value。
  但是以对象作为key是可能会造成内存泄漏的啊,由于Thread对象可能会大量创建,在回收时不removeMap里的value时会导致Map越来越大,最后内存溢出来。Unsafe功能
  不过再仔细看ThreadLocalRandom类似的核心代码,发现并不是简单的Map操作,它的getLong()方法需要传入两个参数,而putLong()方法需要三个参数,查看源码发现它们都是native方法,我们看不到具体的实现。两个方法签名分别是:publicnativelonggetLong(Objectvar1,longvar2);publicnativevoidputLong(Objectvar1,longvar2,longvar4);
  虽然看不到具体实现,但我们可以查得到它们的功能,下面是两个方法的功能介绍:putLong(object,offset,value)可以将object对象内存地址偏移offset后的位置的后四个字节设置为value。getLong(object,offset)会从object对象内存地址偏移offset后的位置读取四个字节作为long型返回。不安全性
  作为Unsafe类内的方法,它也透露着一股Unsafe的气息,具体表现就是可以直接操作内存,而不做任何安全校验,如果有问题,则会在运行时抛出FatalError,导致整个虚拟机的退出。
  在我们的常识里,get方法是最容易抛异常的地方,比如空指针、类型转换等,但Unsafe。getLong()方法是个非常安全的方法,它从某个内存位置开始读取四个字节,而不管这四个字节是什么内容,总能成功转成long型,至于这个long型结果是不是跟业务匹配就是另一回事了。而set方法也是比较安全的,它把某个内存位置之后的四个字节覆盖成一个long型的值,也几乎不会出错。
  那么这两个方法不安全在哪呢?
  它们的不安全并不是在这两个方法执行期间报错,而是未经保护地改变内存,会引起别的方法在使用这一段内存时报错。publicstaticvoidmain(String〔〕args)throwsNoSuchFieldException,IllegalAccessException{Unsafe设置了构造方法私有,getUnsafe获取实例方法包私有,在包外只能通过反射获取FieldfieldUnsafe。class。getDeclaredField(theUnsafe);field。setAccessible(true);Unsafeunsafe(Unsafe)field。get(null);Test类是一个随手写的测试类,只有一个String类型的测试类TesttestnewTest();test。ttt12345;unsafe。putLong(test,12L,2333L);System。out。println(test。value);}
  运行上面的代码会得到一个fatalerror,报错信息为AfatalerrorhasbeendetectedbytheJavaRuntimeEnvironment:Processfinishedwithexitcode134(interruptedbysignal6:SIGABRT)。
  可以从报错信息中看到虚拟机因为这个fatalerrorabort退出了,原因也很简单,我没有使用unsafe将Test类value属性的位置设置成了long型值2333,而当我使用value属性时,虚拟机会将这一块内存解析为String对象,原String对象对象头的结构被打乱了,解析对象失败抛出了错误,更严重的问题是报错信息中没有类名行号等信息,在复杂项目中排查这种问题真如同大海捞针。
  不过Unsafe的其他方法可不一定像这一对方法一样,使用他们时可能需要注意另外的安全问题,之后有遇到再说。ThreadLocalRandom的实现
  那么ThreadLocalRandom是不是安全的呢,再回过头来看一下它的实现。
  ThreadLocalRandom的实现需要Thread对象的配合,在Thread对象内存在着一个属性threadLocalRandomSeed,它保存着这个线程专属的随机种子,而这个属性在Thread对象的offset,是在ThreadLocalRandom类加载时就确定了的,具体方法是SEEDUNSAFE。objectFieldOffset(Thread。class。getDeclaredField(threadLocalRandomSeed));
  我们知道一个对象所占用的内存大小在类被加载后就确定了的,所以使用Unsafe。objectFieldOffset(class,fieldName)可以获取到某个属性在类中偏移量,而在找对了偏移量,又能确定数据类型时,使用ThreadLocalRandom就是很安全的。疑问
  在查找这些问题的过程中,我也产生了两个疑问点。使用场景
  首先就是ThreadLocalRandom为什么非要使用Unsafe来修改Thread对象内的随机种子呢,在Thread对象内添加getset方法不是更方便吗?
  stackOverFlow上有人跟我同样的疑问,whyisthreadlocalrandomimplementedsobizarrely,被采纳的答案里解释说,对jdk开发者来说Unsafe和getset方法都像普通的工具,具体使用哪一个并没有一个准则。这个答案并没有说服我,于是我另开了一个问题,里面的一个评论我比较认同,大意是ThreadLocalRandom和Thread不在同一个包下,如果添加getset方法的话,getset方法必须设置为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。小结
  在写代码时还是要多注意查看依赖库的具体实现,不然可能踩到意想不到的坑,而且多看看并没有坏处,仔细研究一下还能学到更多。
投诉 评论 转载

干掉Random这个类已经成为获取随机数的王者原文链接:https:mp。weixin。qq。comsrYoAyu4pG2GHZYTuKvn8g前言最近在写一些业务代码时遇到一个需要产生随机数的场景,这时自然想到jd……看完这篇,你就知道大家为何放弃液晶电视选择激光电视了激光电视的兴起给家庭用户带来了更多选择,但作为新兴的显示设备,很多人对它还不甚了解,甚至会将它与传统投影混淆。今天我就给大家介绍三个关于激光电视的冷知识,看完你就知道,为什么越……增程式是香饽饽?这几款增程式新车即将上市,合资品牌也凑热闹在中国的新能源车市场,EREV(增程式)技术一直备受争议,毕竟从本质上来看,就是一台纯电动汽车载着一台烧油的发电机在跑,有点脱裤子放屁,多此一举的感觉。但是作为国内增程式电动车……iPhone13Pro价格曝光,高配推出1T容量,果粉真的会今年手机行业友商对于新产品的研发进度比往年要快一点,到目前除了华为往年定位下半年的Mate系列发布不了,其他的大部分硬核旗舰都已经到来了,重要指标小米MIX4的屏下摄像头技术更……最低899元家家户户都值得买的3把智能门锁心里总想买点啥?看看《必买》,全网最有料的场景种草指南。出门不爱带钥匙,但又迫不得已?因为忘了带钥匙,多次请开锁师傅上门,导致跟开锁师傅成了最熟悉的陌生人?家里没人朋友来……男生用iPhoneXS好还是iPhoneXSMax好?我用的就是xsmax还好不是很大,信号真不是很好断网正常,不戴眼镜眼睛真难受,原来7p不会,最近在研究一下怎么设置屏幕,眼睛看一会儿就难受选择iPhoneXSMax比较好……你用过的最美的手机锁屏是什么?都是最近使用过的这些都是我近几次用的,第一张现在在用我用的手机锁屏:是我本人的照片,而且是老公亲自拍的〔得意〕图一和图三用的最久的拼接壁纸最美壁纸。。。……联想小新PadPro12。6明天发布消息显示,11月2日19:30,联想将举办秋季轻薄新品发布会,小新PadPro12。6英寸终于要和大家见面了。小新PadPro12。6配备12。6英寸AMOLED屏幕,屏……被列入预摘牌名单爱奇艺回应积极谋求解决方案保护股东利益新京报贝壳财经讯(记者宋美璐)3月31日,中国证监会国际部负责人在答记者问时指出,对于一些企业被SEC列入有退市风险的清单,经向美国SEC了解,这是美国监管部门执行《外国公司问……机器人传感器分类一般机器人系统由机械手、环境、任务和控制器四个互相作用的部分组成。我们称一般安装在机器人机械手上的传感器为内传感器(InnerSensons),而称作为环境的一部分的传感器为外……割草机器人的爆发机会来了!全球2。5亿个私人花园,谁会成为冠作者丨巴里编辑丨子钺题图丨九号公司在过去20年,最有价值的家用服务机器人产品是什么?答案一定是扫地机器人。近年来,全球扫地机器人市场从年销数十万台……2000以内的手机选这两款红米是小米的子品牌,专做中端机,将性价比发挥到极致。像我们学生或者是经济有限的人对新出的高档手机羡慕却奈何不得,今天推荐的两款的手机都是神机。可以用个四五年,也符合大众的……
太平洋给予立中集团买入评级,目标价位37。2元海尔海信格力三大企业,接连发生变动,是发生了什么?国产车又一春,宏光MINI欧拉好猫都火了,要开启国民小车时代打工人的利器,杜伽K320W无线蓝牙机械键盘评测互联网数字化运营可以推动全渠道增长吗?假如美团外卖关闭了,对我们现在社会是利大于弊还是弊大于利?唱弹俱佳,精彩演绎得胜OP8便携式弹唱音箱强势来袭中国5G射频芯片的突围比EUV光刻机更迫切!美国3巨头垄断95市场,华为已开始布局男生的数码产品可以多到什么程度?波场链支付型代币基因密码即将引领加密货币市场是上学苦还是上班苦?关于腊八节的说说腊八节发朋友圈的经典短语我,河南人,没钱没相貌身高1。5米,本想打光棍,竟娶了老挝美70女生不会选面膜,小心花钱毁脸重磅福利人工智能产品经理的新起点页免费下载三位曾仅次梅罗的世界第三人!生涯荣誉傍身,江湖地位却不高adidasalphaedge4dwc发售时间多少钱?不会选礼物?200元到2000元,玩游戏的男生最想收到的礼物写给同学的诗歌公司的通知财闻联播热搜第一!政协委员大学生就业不要满脑子ampampq辛弃疾满江红饯郑衡州厚卿席上再赋语文教研组学期工作总结多篇

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找七猫云易事利