I want to simulate a clock that ticks on a high frequency in Java. We can think of it as a 1Mhz CPU clock for example. This can be achieved with this code:
// 1000 nanoseconds between ticks.
final long period = 1000;
this.runnable = new Runnable() {
public void run() {
long last = System.nanoTime();
while (true) {
long now = System.nanoTime();
if (now - last > period) {
last = now;
System.out.println(Thread.currentThread().getId() + " ticked at:" + now);
}
}
}
};
this.thread = new Thread(this.runnable);
this.thread.start();
The problem with this code is that it is a high CPU consumer as the thread is running constantly. I want to make the thread to sleep for some time between subsequent time checks, something like the code below:
// 1000 nanoseconds between ticks.
final long period = 1000;
this.runnable = new Runnable() {
public void run() {
long last = System.nanoTime();
while (true) {
long now = System.nanoTime();
if (now - last > period) {
last = now;
System.out.println(Thread.currentThread().getId() + " ticked at:" + now);
}
}
// here 4 is arbitrary, it determines checks frequency
LockSupport.parkNanos(period / 4);
}
};
this.thread = new Thread(this.runnable);
this.thread.start();
The problem in this approach is that the thread is halted for too long (> 1ms in windows). The same thing happens if I use Thread.sleep(0, period / 4);
.
I understand the Thread.sleep
and LockSupport.parkNanos
can only guarantee the minimum time to sleep/halt.
Is there a way to achieve what I want, i.e: tick the lock at this frequency without making the thread to run constantly?
ScheduledExecutorService
In modern Java, we rarely need to address Thread
directly. Instead, use the Executor framework added to Java 5.
Part of that framework is ScheduledExecutorService
. You can ask that a task should be executed repeatedly between a specified period of time.
Some important points:
scheduleAtFixedRate
and scheduleWithFixedDelay
. I suspect you want the first. But study both to decide for yourself.Here is some example code in Java 20.
AtomicLong count = new AtomicLong( 0L );
try (
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor() ;
)
{
Runnable task = count :: incrementAndGet;
ses.scheduleAtFixedRate( task , 0L , 1L , TimeUnit.MICROSECONDS ); // 1,000 nanos = 1 micro.
try { Thread.sleep( Duration.ofMillis( 10 ) ); } catch ( InterruptedException e ) { throw new RuntimeException( e ); }
}
// We expect around 1,000 per millisecond in our count.
System.out.println( "count = " + count );
When run:
count = 13226
We expect about 1,000 count for every millisecond. For ten milliseconds, we would expect about 10,000 in our count. I am getting results of 10,xxx to 13,xxx in the count when run within IntelliJ in Java 20 on an Apple M1 MacBook Pro.