一、背景
撸代码时发现System.currentTimeMillis的调用都被封装成了cache类型,代码如下:
那么System.currentTimeMillis真的有这么这么差吗,如果差的话又是什么原因造成的?什么情况下可以直接调用原生方法,什么情况下需要使用缓存呢?
二、测试
先问会不会,再问为什么。那么System.currentTimeMillis的性能是否真的有这么差,很多大佬给出了自己的测试case并总结出下面两个结论
1)在高并发场景下System.currentTimeMillis()并发问题严重,甚至比创建一个普通对象要耗时的多
2)单线程执行System.currentTimeMillis();比多线程并发执行System.currentTimeMillis();快了许多倍
2.1 单线程场景
大家都在讨论多线程场景下的表现,那么单线程场景下它的表现如何,测试代码如下:
分别跑System.currentTimeMillis 和new 简单对象1k,1w,10w次,测试结果如下:
amazing了,System.currentTimeMillis 竟然在单线程下的性能也比new一个对象差那么多
2.2 多线程场景
看起来线程数和平均耗费时间成正比例,串行处理
三、原理
参考:http://pzemtsov.github.io/2017/07/23/the-slow-currenttimemillis.html
挖源码就到此为止,因为已经有国外大佬深入到了汇编的级别来探究,简单来讲就是:
- 调用gettimeofday()需要从用户态切换到内核态;
- gettimeofday()的表现受Linux系统的计时器(时钟源)影响,在HPET计时器下性能尤其差;
- 系统只有一个全局时钟源,高并发或频繁访问会造成严重的争用。
所以在调用次数少的时候是可以忍受的,效率不会太慢。调用次数多的时候产生竞争才会造成耗时变长。
nanoTime也并不能解决问题。
查看linux系统使用的数据源:
四、解决方法
1)如开头,缓存时钟
2)更改时钟源