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

io.debezium.util.Metronome Maven / Gradle / Ivy

The newest version!
/*
 * Copyright Debezium Authors.
 *
 * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
 */
package io.debezium.util;

import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * A class that can be used to perform an action at a regular interval. To use, set up a {@code Metronome} instance and perform
 * a repeated event (perhaps using a loop), calling {@link #pause()} before each event.
 *
 * @author Randall Hauch
 */
@FunctionalInterface
public interface Metronome {

    /**
     * Pause until the next tick of the metronome.
     *
     * @throws InterruptedException if the thread was interrupted while pausing
     */
    public void pause() throws InterruptedException;

    /**
     * Create a new metronome that starts ticking immediately and that uses {@link Thread#sleep(long)} to wait.
     * 

* Generally speaking, this is a simple but inaccurate approach for periods anywhere close to the precision of the supplied * Clock (which for the {@link Clock#system() system clock} is typically around 10-15 milliseconds for modern Linux and OS X * systems, and potentially worse on Windows and older Linux/Unix systems. And because this metronome uses * Thread#sleep(long), thread context switches are likely and will negatively affect the precision of the metronome's * period. *

* Although the method seemingly supports taking {@link TimeUnit#MICROSECONDS} and {@link TimeUnit#NANOSECONDS}, it is * likely that the JVM and operating system do not support such fine-grained precision. And as mentioned above, care should * be used when specifying a {@code period} of 20 milliseconds or smaller. * * @param period the period of time that the metronome ticks and for which {@link #pause()} waits * @param timeSystem the time system that will provide the current time; may not be null * @return the new metronome; never null */ public static Metronome sleeper(Duration period, Clock timeSystem) { long periodInMillis = period.toMillis(); return new Metronome() { private long next = timeSystem.currentTimeInMillis() + periodInMillis; @Override public void pause() throws InterruptedException { for (;;) { final long now = timeSystem.currentTimeInMillis(); if (next <= now) { break; } Thread.sleep(next - now); } next = next + periodInMillis; } @Override public String toString() { return "Metronome (sleep for " + periodInMillis + " ms)"; } }; } /** * Create a new metronome that starts ticking immediately and that uses {@link LockSupport#parkNanos(long)} to wait. *

* {@link LockSupport#parkNanos(long)} uses the underlying platform-specific timed wait mechanism, which may be more * accurate for smaller periods than {@link #sleeper(long, TimeUnit, Clock)}. However, like * {@link #sleeper(long, TimeUnit, Clock)}, the resulting Metronome may result in thread context switches. *

* Although the method seemingly supports taking {@link TimeUnit#MICROSECONDS} and {@link TimeUnit#NANOSECONDS}, it is * likely that the JVM and operating system do not support such fine-grained precision. And as mentioned above, care should * be used when specifying a {@code period} of 10-15 milliseconds or smaller. * * @param period the period of time that the metronome ticks and for which {@link #pause()} waits * @param timeSystem the time system that will provide the current time; may not be null * @return the new metronome; never null */ public static Metronome parker(Duration period, Clock timeSystem) { long periodInNanos = period.toNanos(); return new Metronome() { private long next = timeSystem.currentTimeInNanos() + periodInNanos; @Override public void pause() throws InterruptedException { while (next > timeSystem.currentTimeInNanos()) { LockSupport.parkNanos(next - timeSystem.currentTimeInNanos()); if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } } next = next + periodInNanos; } @Override public String toString() { return "Metronome (park for " + TimeUnit.NANOSECONDS.toMillis(periodInNanos) + " ms)"; } }; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy