All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.threadly.util.Clock Maven / Gradle / Ivy

package org.threadly.util;

import java.util.concurrent.locks.LockSupport;

/**
 * This is a utility class for low-resolution timing which avoids frequent 
 * {@link System#currentTimeMillis()} calls (which perform poorly because they require system 
 * calls).
 * 

* Each call to {@link Clock#lastKnownTimeMillis()} will return the value of * {@link System#currentTimeMillis()} as of the last call to {@link Clock#accurateTimeMillis()}. * This means {@link #lastKnownTimeMillis()} will only be as accurate as the frequency with which * {@link #accurateTimeMillis()} is called. *

* In order to ensure a minimum level of accuracy, by default a thread is started to call * {@link #accurateTimeMillis()} every 100 milliseconds. This can be disabled by calling * {@link #stopClockUpdateThread()}. * * @since 1.0.0 */ public class Clock { /** * Simple conversion of how many nanoseconds exist within a single millisecond. * * @since 3.1.0 */ public static final int NANOS_IN_MILLISECOND = 1_000_000; /** * This is the frequency at which the thread which regularly updates the clock wakes up and * updates the time. Invocations to {@link #accurateForwardProgressingMillis()} and * {@link #accurateTimeMillis()} both update their respective times, but this will make sure * that even if those calls are not made requests to {@link #lastKnownForwardProgressingMillis()} * and {@link #lastKnownTimeMillis()} have at least some level of accuracy. * * @since 4.0.0 */ public static final short AUTOMATIC_UPDATE_FREQUENCY_IN_MS = 100; protected static final short STOP_PARK_TIME_NANOS = 25000; protected static final Object UPDATE_LOCK = new Object(); protected static ClockUpdater clockUpdater = null; protected static final long CLOCK_STARTUP_TIME_NANOS = System.nanoTime(); private static volatile long nowNanos = CLOCK_STARTUP_TIME_NANOS; private static volatile long nowMillis = System.currentTimeMillis(); static { startClockUpdateThread(); } /** * Starts a thread to regularly updated the clock automatically. */ public static void startClockUpdateThread() { synchronized (UPDATE_LOCK) { if (clockUpdater != null) { return; } else { clockUpdater = new ClockUpdater(); Thread thread = new Thread(clockUpdater); thread.setName("Threadly clock updater"); // name referenced in Profiler thread.setDaemon(true); thread.start(); } } } /** * Stops the clock from updating automatically. * * This call blocks until the automatic update thread stops, or until this thread is interrupted. */ public static void stopClockUpdateThread() { ClockUpdater oldUpdater; synchronized (UPDATE_LOCK) { oldUpdater = clockUpdater; clockUpdater = null; UPDATE_LOCK.notifyAll(); } if (oldUpdater != null) { Thread currentThread = Thread.currentThread(); while (! oldUpdater.runnableFinished && ! currentThread.isInterrupted()) { LockSupport.parkNanos(STOP_PARK_TIME_NANOS); } } } /** * This directly returns the result of {@link System#nanoTime()}. Using this as an alternative * to invoking {@link System#nanoTime()} directly is that it updates the nano time * representation, allowing for more accurate time references when calling * {@link #lastKnownTimeNanos()} and {@link Clock#lastKnownForwardProgressingMillis()}. *

* Please read the java documentation about {@link System#nanoTime()} to understand the nature * of this value (it may be positive, negative, overflow, and is completely arbitrary from its * start point). * * @return a long which is a constantly forward moving representation of nano seconds */ public static long accurateTimeNanos() { return nowNanos = System.nanoTime(); } /** * This returns a fuzzy known nano time from invocations to either {@link #accurateTimeNanos()} * or {@link #accurateForwardProgressingMillis()}. In addition (unless manually stopped via * {@link #stopClockUpdateThread()}) this time is updated at the frequency of * {@link #AUTOMATIC_UPDATE_FREQUENCY_IN_MS}. Thus providing a minimal level of accuracy. *

* Please read the java documentation about {@link System#nanoTime()} to understand the nature * of this value (it may be positive, negative, overflow, and is completely arbitrary from its * start point). * * @return a long which is a constantly forward moving representation of nano seconds */ public static long lastKnownTimeNanos() { return nowNanos; } /** * Returns a fuzzy time for how much time in milliseconds since this class has loaded (starting * at {@code 0}). If {@link Clock} was loaded at the start of the application, this can provide * the amount of time the application has been running. *

* This call is guaranteed to only progress forward, regardless of system clock changes it will * move forward at a consistent rate. *

* By default (unless manually stopped via {@link #stopClockUpdateThread()}) this time is * updated automatically at the frequency of {@link #AUTOMATIC_UPDATE_FREQUENCY_IN_MS}. Thus * allowing a guarantee of minimal accuracy within the set milliseconds. * * @since 3.1.0 * @return Amount of time in milliseconds since Clock class was loaded */ public static long lastKnownForwardProgressingMillis() { /* We can not guarantee that nowNanos is > CLOCK_STARTUP_TIME_NANOS, since the nano time may * overflow. But subtracting after an overflow, will still produce a positive result. */ return (nowNanos - CLOCK_STARTUP_TIME_NANOS) / NANOS_IN_MILLISECOND; } /** * Returns an accurate amount of time in milliseconds since this class has loaded (starting at * {@code 0}). If {@link Clock} was loaded at the start of the application, this can provide * the amount of time the application has been running. Calls to this will NOT update the time * in {@link #accurateTimeMillis()}. *

* This call is guaranteed to only progress forward, regardless of system clock changes it will * move forward at a consistent rate. * * @since 3.1.0 * @return Amount of time in milliseconds since Clock class was loaded */ public static long accurateForwardProgressingMillis() { return ((nowNanos = System.nanoTime()) - CLOCK_STARTUP_TIME_NANOS) / NANOS_IN_MILLISECOND; } /** * Finds the duration in milliseconds from the reference time. Effectively this is the same as * subtracting the result from {@link #lastKnownForwardProgressingMillis()} from the parameter * provided. The parameter provided should have been a result from * {@link #lastKnownForwardProgressingMillis()} or {@link #accurateForwardProgressingMillis()}. * Manipulating that time before calling this is not supported (negative results will not be * provided). This is simply a helper function for this very common operation. * * @param forwardMillis The reference time * @return The milliseconds from the provided reference time */ public static long forwardProgressingDuration(long forwardMillis) { return Math.max(0, lastKnownForwardProgressingMillis() - forwardMillis); } /** * Getter for the last known time in milliseconds. This time is considered semi-accurate, based * off the last time accurate time has been requested, or this class has automatically updated * the time (unless requested to stop automatically updating). *

* If the system clock goes backwards this too can go backwards. If that is not desirable * consider using {@link #lastKnownForwardProgressingMillis()}. *

* By default (unless manually stopped via {@link #stopClockUpdateThread()}) this time is * updated automatically at the frequency of {@link #AUTOMATIC_UPDATE_FREQUENCY_IN_MS}. Thus * allowing a guarantee of minimal accuracy within the set milliseconds. * * @return last known time in milliseconds */ public static long lastKnownTimeMillis() { return nowMillis; } /** * Updates the clock so that future calls to {@link #lastKnownTimeMillis()} can benefit, and * returns the accurate time in milliseconds. This will NOT update the time for calls to * {@link #lastKnownForwardProgressingMillis()}. *

* If the system clock goes backwards this too can go backwards. If that is not desirable * consider using {@link #accurateForwardProgressingMillis()}. * * @since 2.0.0 (since 1.0.0 as accurateTime) * @return accurate time in milliseconds */ public static long accurateTimeMillis() { return nowMillis = System.currentTimeMillis(); } /** * Runnable which will regularly update the stored clock time. This runnable is designed to run * in its own dedicated thread. * * @since 1.0.0 */ protected static class ClockUpdater implements Runnable { protected volatile boolean runnableFinished = false; protected long lastUpdatedMillis = -1; protected long lastUpdatedNanos = -1; @Override public void run() { try { synchronized (UPDATE_LOCK) { while (clockUpdater == this) { try { if (nowMillis == lastUpdatedMillis || // check if task is not waking up as expected, if not lets update while we have cpu time nowMillis - lastUpdatedMillis > AUTOMATIC_UPDATE_FREQUENCY_IN_MS) { nowMillis = lastUpdatedMillis = System.currentTimeMillis(); } else { lastUpdatedMillis = nowMillis; } if (nowNanos == lastUpdatedNanos || // check if task is not waking up as expected, if not lets update while we have cpu time nowNanos - lastUpdatedNanos > AUTOMATIC_UPDATE_FREQUENCY_IN_MS * NANOS_IN_MILLISECOND) { nowNanos = lastUpdatedNanos = System.nanoTime(); } else { lastUpdatedNanos = nowNanos; } UPDATE_LOCK.wait(AUTOMATIC_UPDATE_FREQUENCY_IN_MS); } catch (InterruptedException e) { clockUpdater = null; // let thread exit Thread.currentThread().interrupt(); } } } } finally { runnableFinished = true; } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy