System.currentTimeMillis的性能,真有如此不堪吗?
码农突围
共 7833字,需浏览 16分钟
· 2021-07-27
点击上方“码农突围”,马上关注 这里是码农充电第一站,回复“666”,获取一份专属大礼包 真爱,请设置“星标”或点个“在看
/**
* 弱精度的计时器,考虑性能不使用同步策略。
*
* @author mycat
*/
public class TimeUtil {
//当前毫秒数的缓存
private static volatile long CURRENT_TIME = System.currentTimeMillis();
public static final long currentTimeMillis() { return CURRENT_TIME; }
public static final long currentTimeNanos() { return System.nanoTime(); }
//更新缓存
public static final void update() { CURRENT_TIME = System.currentTimeMillis(); }
}
//使用定时任务调度线程池,定期(每1s)调用update方法更新缓存时钟
heartbeatScheduler.scheduleAtFixedRate(processorCheck(), 0L, 1000, TimeUnit.MILLISECONDS);
# 思索,System.currentTimeMillis有什么性能问题
System.currentTimeMillis要访问系统时钟,这属于临界区资源,并发情况下必然导致多线程的争用 System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道 我有测试记录,并发耗时就是比单线程高250倍!
大家可以把顺序锁当成是解决了“ABA问题”的CompareAndSwap锁。对于一个临界区资源(这里是xtime),有一个操作序列号,写操作会使序列号+1,读操作则不会。 写操作:CAS使序列号+1 读操作:先获取序列号,读取数据,再获取一次序列号,前后两次获取的序列号相同,则证明进行读操作时没有写操作干扰,那么这次读是有效的,返回数据,否则说明读的时侯可能数据被更改了,这次读无效,重新做读操作。 大家可能有个疑问:读xtime的时候数据可能被更改吗?难度读操作不是原子性的吗?这是因为xtime是64位的,对于32位机器是需要分两次读的,而64位机器不会产生这个并发的问题。
long begin = System.nanoTime();
//单次调用System.currrentTimeMillis()
long end = System.nanoTime();
sum += end - begin;
记录每次调用的总耗时,这种方法虽然会把System.nanoTime()也算进总耗时里,但因为不论并发测试还是单线程测试都会记录System.nanoTime(),不会导致测试的不公平
# 数据说话,System.currentTimeMillis的性能没有问题
System代表 System.currentTimeMillis 缓存时钟代表 使用静态成员变量做System.currentTimeMillis缓存的时钟类 200线程-Tomcat的默认线程数
使用JMH(Java基准测试框架)的测试结果
JMH按照推荐使用了双倍CPU的线程数(8线程),统计的是平均时间,测试代码见文末
测试结果分析
这里没有做“new一个对象”的测试,是因为并不是代码里写了new Object(),JVM就会真的会给你在堆内存里new一个对象。这是JVM的一个编译优化——逃逸分析:先分析要创建的对象的作用域,如果这个对象只在一个method里有效(局部变量对象),则属于未 方法逃逸,不去实际创建对象,而是你在method里调了对象的哪个方法,就把这个方法的代码块内联进来。只在线程内有效则属于未 线程逃逸,会创建对象,但会自动消除我们做的无用的同步措施。
# 最后
测试代码:
public class CurrentTimeMillisTest {
public static void main(String[] args) {
int num = 10000000;
System.out.print("单线程"+num+"次System.currentTimeMillis调用总耗时:");
System.out.println(singleThreadTest(() -> {
long l = System.currentTimeMillis();
},num));
System.out.print("单线程"+num+"次CacheClock.currentTimeMillis调用总耗时:");
System.out.println(singleThreadTest(() -> {
long l = CacheClock.currentTimeMillis();
},num));
System.out.print("并发"+num+"次System.currentTimeMillis调用总耗时:");
System.out.println(concurrentTest(() -> {
long l = System.currentTimeMillis();
},num));
System.out.print("并发"+num+"次CacheClock.currentTimeMillis调用总耗时:");
System.out.println(concurrentTest(() -> {
long l = CacheClock.currentTimeMillis();
},num));
}
/**
* 单线程测试
* @return
*/
private static long singleThreadTest(Runnable runnable,int num) {
long sum = 0;
for (int i = 0; i < num; i++) {
long begin = System.nanoTime();
runnable.run();
long end = System.nanoTime();
sum += end - begin;
}
return sum;
}
/**
* 并发测试
* @return
*/
private static long concurrentTest(Runnable runnable,int num) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(200,200,60, TimeUnit.SECONDS,new LinkedBlockingQueue<>(num));
long[] sum = new long[]{0};
//闭锁基于CAS实现,并不适合当前的计算密集型场景,可能导致等待时间较长
CountDownLatch countDownLatch = new CountDownLatch(num);
for (int i = 0; i < num; i++) {
threadPoolExecutor.submit(() -> {
long begin = System.nanoTime();
runnable.run();
long end = System.nanoTime();
//计算复杂型场景更适合使用悲观锁
synchronized(CurrentTimeMillisTest.class) {
sum[0] += end - begin;
}
countDownLatch.countDown();
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return sum[0];
}
/**
* 缓存时钟,缓存System.currentTimeMillis()的值,每隔20ms更新一次
*/
public static class CacheClock{
//定时任务调度线程池
private static ScheduledExecutorService timer = new ScheduledThreadPoolExecutor(1);
//毫秒缓存
private static volatile long timeMilis;
static {
//每秒更新毫秒缓存
timer.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
timeMilis = System.currentTimeMillis();
}
},0,1000,TimeUnit.MILLISECONDS);
}
public static long currentTimeMillis() {
return timeMilis;
}
}
}
使用JMH的测试代码:
(Mode.AverageTime)
(TimeUnit.MICROSECONDS)
//120轮预热,充分利用JIT的编译优化技术
120,time = 1,timeUnit = TimeUnit.MILLISECONDS) (iterations =
1,timeUnit = TimeUnit.MICROSECONDS) (time =
//线程数:CPU*2(计算复杂型,也有CPU+1的说法)
8) (
1) (
(Scope.Benchmark)
public class JMHTest {
public static void main(String[] args) throws RunnerException {
testNTime(10000);
}
private static void testNTime(int num) throws RunnerException {
Options options = new OptionsBuilder()
.include(JMHTest.class.getSimpleName())
.measurementIterations(num)
.output("E://testRecord.log")
.build();
new Runner(options).run();
}
/**
* System.currentMillisTime测试
* @return 将结果返回是为了防止死码消除(编译器将 无引用的变量 当成无用代码优化掉)
*/
public long testSystem() {
return System.currentTimeMillis();
}
/**
* 缓存时钟测试
* @return
*/
public long testCacheClock() {
return JMHTest.CacheClock.currentTimeMillis();
}
/**
* 缓存时钟,缓存System.currentTimeMillis()的值,每隔1s更新一次
*/
public static class CacheClock{
private static ScheduledExecutorService timer = new ScheduledThreadPoolExecutor(1);
private static volatile long timeMilis;
static {
timer.scheduleAtFixedRate(new Runnable() {
public void run() {
timeMilis = System.currentTimeMillis();
}
},0,1000,TimeUnit.MILLISECONDS);
}
public static long currentTimeMillis() {
return timeMilis;
}
}
}
- END - 最近热文
• 华为天才少年造出自动驾驶单车!图纸已开源,硬件成本一万,B站老板:重新定义「自行」车 • 张一鸣的大学四年收获及工作感悟 • 张一鸣:为什么 BAT 挖不走我们的人? • 2021年7月编程语言排行榜
评论
真高!比亚迪员工爆料比亚迪在越南的薪资水平:基本工资480万,全勤奖35万,交通补助20万,餐补110万,每周6天,每天10小时
上一篇:某大公司为逼迫员工离职,竟然把他的工位安排到厕所旁,没想到他直接开始记录领导的如厕时间,还发到公司大群...对此,你怎么看?--完--PS:欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,欢迎转发分享给更多人。全文完,感谢你的耐心阅读。如果你还想看到我的文章,请一定给本
开发者全社区
0
太敢穿了!透视纱裙!性感火辣的身材
绝了呀今天的厂花:吴宣仪1995年1月26日,吴宣仪出生于海南省海口市,中国内地流行乐女歌手、影视演员。2016年2月,吴宣仪随宇宙少女发行首张迷你专辑正式出道。2018年4月,她参加《创造101》综艺选秀,获得第二名,成功加入火箭少女101组合。吴宣仪的颜值一直备受称赞,她的五官立体精致,皮肤白皙
逆锋起笔
0
某大公司为逼迫员工离职,竟然把他的工位安排到厕所旁,没想到他直接开始记录领导的如厕时间,还发到公司大群...
上一篇:字节的跳动职级与薪资(2024年)我们与公司间的合作,宛如两艘船只在茫茫大海上相互依靠,共同抵御风浪,携手驶向成功的彼岸。然而,当航向开始产生分歧,或是波涛汹涌的风浪改变了我们的初衷,我们或许应当冷静地选择和平分手,而非在风雨中硬撑。最近,一位网友的遭遇引起了广大职场人的关注和热议。这位网友
开发者全社区
0
金融研究 | 使用Python测量关键审计事项的「信息含量」
Tips: 公众号推送后内容只能更改一次,且只能改20字符。如果内容出问题,或者想更新内容, 只能重复推送。为了更好的阅读体验,建议阅读本文博客版, 链接地址https://textdata.cn/blog/2023-01-13-information-content-of-critical-aud
大邓和他的Python
0
我看阿里的年终奖总算发了!
到4月底了,这两天看朋友圈,发现阿里的年终奖终于发了,问了问老同学,也从网上检索了不少信息,基本搞清楚了阿里今年的年终奖情况。近来来阿里一些集团对绩效等级做了较大的调整,以前的旧绩效系统中,绩效分为3.25、3.5、3.75、4和5五个等级,其中4和5是较高绩效等级,较少见。而且之前3.5绩效内部划
公子龙
0
CVPR 2024|大视觉模型的开山之作!无需任何语言数据即可打造大视觉模型
↑ 点击蓝字 关注极市平台作者丨科技猛兽编辑丨极市平台极市导读 本文提出一种序列建模 (sequential modeling) 的方法,不使用任何语言数据,训练大视觉模型。>>加入极市CV技术交流群,走在计算机视觉的最前沿本文目录1 序列建模打造大视觉模型(来自 U
极市平台
1
金融研究(更新) | 使用Python构建关键审计事项的「信息含量」
Tips: 公众号推送后内容只能更改一次,且只能改20字符。如果内容出问题,或者想更新内容, 只能重复推送。为了更好的阅读体验,建议阅读本文博客版, 链接地址https://textdata.cn/blog/2023-01-13-information-content-of-critical-aud
大邓和他的Python
0
字节的跳动职级与薪资(2024年)
上一篇:阿里公布年终奖,P7, 3.5+,22W年终奖,还有35W长期现金激励,真香字节跳动自2012年3月成立以来,已经迅速成长为一个全球性的科技公司。其产品和服务已经遍布全球150多个国家与地区,并且支持超过75种不同的语言。在字节跳动的官方网站上,列出了一系列引人注目的产品和服务,包括但不限于
开发者全社区
0