System.currentTimeMillis()高并发性能优化
摘要:System.currentTimeMillis()性能问题的研究、测试与优化。
性能优化使用的测试环境:
jdk版本jdk8
操作系统:
- macOS
- 版本:13.2.1
- 芯片: Apple M1
- CPU核数:8核
System.currentTimeMillis()是Java极其常用的 API,广泛地用来获取时间戳或统计代码执行耗时等,在我们的印象中应该快如闪电。但实际上在高并发、低延时的情况下,其性能表现令人大跌眼镜,调用开销明显变高。
public static void main(String[] args) throws Exception {
singleThreadTest();
multiThreadTest();
}
public static void singleThreadTest() {
//测试一百次循环,每次循环调用System.currentTimeMillis()1千万次数
for (int t = 0; t < 100; t++) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//获取一千万次时间
for (int i = 0; i < 10000000; i++) {
System.currentTimeMillis();
}
stopWatch.stop();
System.out.println(stopWatch.getTotalTimeMillis());
}
}
public static void multiThreadTest() throws Exception {
//100个线程各执行一次
CountDownLatch wait = new CountDownLatch(1);
int loopNum = 100;
CountDownLatch threadLatch = new CountDownLatch(loopNum);
for (int i = 0; i < loopNum; i++) {
new Thread(() -> {
try {
StopWatch watch = new StopWatch();
//先阻塞住所有线程
wait.await();
watch.start();
for (int j = 0; j < 10000; j++) {
System.currentTimeMillis();
}
watch.stop();
System.out.println(watch.getTotalTimeNanos());
} catch (InterruptedException e) {
} finally {
threadLatch.countDown();
}
}).start();
}
wait.countDown();
threadLatch.await();
}
执行后,控制台打印的部分执行结果如下:
由此可见,单线程执行System.currentTimeMillis()比多线程并发执行快了太多倍。至于为什么这么慢,感兴趣的童鞋可以去问问度娘,这里给出一个基于定时任务按照毫秒更新缓存时间戳的方案,也就是在内存中维护一个全局缓存,其它线程取时间戳时从内存读取,代价就是牺牲了一些精确度。具体代码如下:
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* @Author Wiener
* @Date 2023-08-12
* @Description: 缓存系统时间
*/
public class SystemClock {
private final int period;
private final AtomicLong now;
private static final String THREAD_NAME ="wienerClock";
private static class InstanceHolder {
private static final SystemClock INSTANCE = new SystemClock(1);
}
private SystemClock(int period) {
this.period = period;
this.now = new AtomicLong(System.currentTimeMillis());
scheduleClockUpdating();
}
private static SystemClock instance() {
return InstanceHolder.INSTANCE;
}
/**
* 供消费者调用,以替换原来的System.currentTimeMillis()
*/
public static long now() {
return instance().now.get();
}
private void scheduleClockUpdating() {
ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(1, r -> {
Thread thread = new Thread(r, THREAD_NAME);
thread.setDaemon(true);
return thread;
});
// 每毫秒获取一次系统时间,并赋值给 AtomicLong now
scheduler.scheduleAtFixedRate(() -> now.set(System.currentTimeMillis()), period, period, TimeUnit.MILLISECONDS);
}
}
在并发量大的情况下,使用SystemClock.now()
输出当前时间,有一定精度损失,但是提高了系统时间获取效率。
温馨提示,在System.currentTimeMillis()的效率没有影响程序整体吞吐量时,没有必要做这种优化,这只是为高并发情况准备的。
读后有收获,小礼物走一走,请作者喝咖啡。
Buy me a coffee. ☕Get red packets.
作者:楼兰胡杨
本文版权归作者和博客园共有,欢迎转载,但请注明原文链接,并保留此段声明,否则保留追究法律责任的权利。