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.作者:樓蘭胡楊
本文版權歸作者和博客園共有,歡迎轉載,但請注明原文鏈接,并保留此段聲明,否則保留追究法律責任的權利。

浙公網安備 33010602011771號