Why System.currentTimeMillis Slows Down Under High Concurrency and How to Fix It

The article reveals that frequent or concurrent calls to Java's System.currentTimeMillis can become dramatically slower due to kernel transitions and clock‑source contention, demonstrates the slowdown with benchmark code, explains the native gettimeofday implementation, and proposes a cached‑timestamp solution using a single scheduler thread.

Programmer DD
Programmer DD
Programmer DD
Why System.currentTimeMillis Slows Down Under High Concurrency and How to Fix It

System.currentTimeMillis() is a widely used Java API for obtaining timestamps, but under high concurrency or extremely frequent calls its performance can degrade dramatically.

public class CurrentTimeMillisPerfDemo {
    private static final int COUNT = 100;
    public static void main(String[] args) throws Exception {
        long beginTime = System.nanoTime();
        for (int i = 0; i < COUNT; i++) {
            System.currentTimeMillis();
        }
        long elapsedTime = System.nanoTime() - beginTime;
        System.out.println("100 System.currentTimeMillis() serial calls: " + elapsedTime + " ns");
        CountDownLatch startLatch = new CountDownLatch(1);
        CountDownLatch endLatch = new CountDownLatch(COUNT);
        for (int i = 0; i < COUNT; i++) {
            new Thread(() -> {
                try {
                    startLatch.await();
                    System.currentTimeMillis();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    endLatch.countDown();
                }
            }).start();
        }
        beginTime = System.nanoTime();
        startLatch.countDown();
        endLatch.await();
        elapsedTime = System.nanoTime() - beginTime;
        System.out.println("100 System.currentTimeMillis() parallel calls: " + elapsedTime + " ns");
    }
}

The benchmark shows that 100 parallel calls take about 250 times longer than 100 serial calls, and the cost can exceed that of creating a simple object. The root cause lies in the native implementation of System.currentTimeMillis() on Linux.

jlong os::javaTimeMillis() {
    timeval time;
    int status = gettimeofday(&time, NULL);
    assert(status != -1, "linux error");
    return jlong(time.tv_sec) * 1000 + jlong(time.tv_usec / 1000);
}

Calling gettimeofday() requires a transition from user mode to kernel mode, and its performance depends on the system's clock source. On systems using the HPET timer, the call is especially slow because all timestamp requests are serialized. Moreover, the system has a single global clock source, causing contention under high concurrency.

gettimeofday() incurs a user‑to‑kernel switch.

Performance varies with the Linux timer source; HPET performs poorly.

A single global clock source leads to severe contention when accessed frequently.

One common mitigation is to maintain a cached timestamp updated by a dedicated scheduler thread, allowing other threads to read the time from memory without invoking the kernel.

public class CurrentTimeMillisClock {
    private volatile long now;
    private CurrentTimeMillisClock() {
        this.now = System.currentTimeMillis();
        scheduleTick();
    }
    private void scheduleTick() {
        new ScheduledThreadPoolExecutor(1, r -> {
            Thread thread = new Thread(r, "current-time-millis");
            thread.setDaemon(true);
            return thread;
        }).scheduleAtFixedRate(() -> {
            now = System.currentTimeMillis();
        }, 1, 1, TimeUnit.MILLISECONDS);
    }
    public long now() { return now; }
    public static CurrentTimeMillisClock getInstance() {
        return SingletonHolder.INSTANCE;
    }
    private static class SingletonHolder {
        private static final CurrentTimeMillisClock INSTANCE = new CurrentTimeMillisClock();
    }
}

Usage: CurrentTimeMillisClock.getInstance().now(). This optimization is only worthwhile when the cost of System.currentTimeMillis() impacts overall performance; otherwise, the added complexity is unnecessary.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaClockSourceSystem.currentTimeMillis
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.