System.currenttimemillis () is an extremely common base Java API, widely used to get timestamps or measure code execution times and so on, which should strike us as lightning fast.

But in practice, when it is called concurrently or very frequently (such as a busy interface, or a high-throughput streaming program that requires a timestamp), its performance can be surprising.

Take a look at the following Demo:

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"); }}Copy the code

The following figure shows the result.

As you can see, a concurrent call to System.CurrentTimemillis () one hundred times takes 250 times longer than a single thread call of one hundred times.

A similar pattern is observed if the frequency of calls to a single thread increases (to the point of, say, several times per millisecond). Follow the public account Java Technology stack for JVM and multithreading and more interview questions and answers.

In fact, in extreme cases, System.CurrentTimemillis () can take more time than creating a simple object instance, so you can try to replace the thread statement with something like newHashMap<>.

Why is that?

Came to the HotSpot source HotSpot/SRC/Linux OS / / vm/os_linux CPP file, there is a javaTimeMillis () method, which is System. CurrentTimeMillis () native to implement.

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); }Copy the code

Dig source code so far, because there have been foreign big guy in-depth to The level of assembly to explore, details can see The Slow currentTimeMillis() this article, I will not teach a fish to fish.

Pzemtsov. Making. IO / 2017/07/23 /…

To put it simply:

  • Calling getTimeofday () requires switching from user mode to kernel mode;

  • Gettimeofday () is affected by Linux timers (clock sources), especially under HPET timers;

  • The system has only one global clock source. High concurrency or frequent access may cause serious contention.

The HPET timer performs poorly because all requests for timestamps are executed serially. TSC timers perform better because they have special registers to hold timestamps. The downside is that it can be unstable, since it is a purely hardware timer with a variable frequency (related to the processor’s CLK signal).

Details on HPET and TSC can be found at:

En.wikipedia.org/wiki/High\_… En.wikipedia.org/wiki/Time\_…

In addition, you can use the following command to view and modify the clock source.

~ cat /sys/devices/system/clocksource/clocksource0/available_clocksource
tsc hpet acpi_pm
~ cat /sys/devices/system/clocksource/clocksource0/current_clocksource
tsc
~ echo 'hpet' > /sys/devices/system/clocksource/clocksource0/current_clocksource
Copy the code

How to solve this problem?

The most common approach is to update the timestamp by millisecond with a single scheduling thread, which is equivalent to maintaining a global cache. Other threads take timestamps from within, eliminating contention for clock resources at the cost of some precision.

The specific code is as follows:

public class CurrentTimeMillisClock { private volatile long now; private CurrentTimeMillisClock() { this.now = System.currentTimeMillis(); scheduleTick(); } private void scheduleTick() { new ScheduledThreadPoolExecutor(1, runnable -> { Thread thread = new Thread(runnable, "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(); }}Copy the code

When using direct CurrentTimeMillisClock. GetInstance (). Now it is ok to ().

However, if the efficiency of System.currentTimemillis () does not affect the overall efficiency of the program, there is no need to rush into optimizations, which are only for extreme cases.

莬 waste value broadcast class, get on the car please SAO ma (days + after please note code: 76)