Code
io.github.coderodde.util.Task.java:
package io.github.coderodde.util;
import java.util.Objects;
/**
* A simple task class.
*
* @author Rodion "rodde" Efremov
* @version 1.0.0 ()
* @since 1.0.0 ()
*/
public class Task {
private final Runnable runnable;
private final long waitTimeNanos;
public Task(final Runnable runnable,
final long waitTimeNanos) {
this.runnable = Objects.requireNonNull(runnable,
"The input runnable is null");
this.waitTimeNanos = waitTimeNanos;
}
void run() {
runnable.run();
}
long getWaitTimeNanos() {
return waitTimeNanos;
}
}
io.github.coderodde.util.TaskScheduler.java:
package io.github.coderodde.util;
import java.time.Duration;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* A simple task scheduler.
*
* @author Rodion "rodde" Efremov
* @version 1.0.0 (Dec 21, 2025)
* @since 1.0.0 (Dec 21, 2025)
*/
public class TaskScheduler implements AutoCloseable {
private static final long MAXIMUM_WAIT_NANOS = 1_000_000_000L;
private final Set<DispatcherThread> dispatcherThreadSet =
ConcurrentHashMap.newKeySet();
public void schedule(final Task task) {
final DispatcherThread dispatcherThread = new DispatcherThread(task);
dispatcherThread.start();
dispatcherThreadSet.add(dispatcherThread);
}
@Override
public void close() throws Exception {
for (final DispatcherThread dispatcherThread : dispatcherThreadSet) {
dispatcherThread.close();
}
}
private final class DispatcherThread extends Thread {
private long sleepDurationNanoseconds;
private final Task task;
private volatile boolean cancelled = false;
DispatcherThread(final Task task) {
this.task = task;
this.sleepDurationNanoseconds = task.getWaitTimeNanos();
}
void close() {
cancelled = true;
}
public void run() {
while (true) {
final long waitNanos = Math.min(MAXIMUM_WAIT_NANOS,
sleepDurationNanoseconds);
if (waitNanos == 0L) {
break;
}
if (cancelled) {
return;
}
sleepDurationNanoseconds -= waitNanos;
try {
Thread.sleep(Duration.ofNanos(waitNanos));
} catch (final Exception ex) {
}
}
if (cancelled) {
return;
}
task.run();
dispatcherThreadSet.remove(this);
}
}
}
io.github.coderodde.util.TaskScheduler.demo.Demo.java:
package io.github.coderodde.util.demo;
import io.github.coderodde.util.Task;
import io.github.coderodde.util.TaskScheduler;
/**
* A small task scheduler demo.
*
* @author Rodion "rodde" Efremov
* @version 1.0.0 (Dec 21, 2025)
* @since 1.0.0 (Dec 21, 2025)
*/
public class Demo {
public static void main(String[] args) throws Exception {
final TaskScheduler scheduler = new TaskScheduler();
final Task task1 = new Task(() -> System.out.println("hello"),
2_000_000_000L);
final Task task2 = new Task(() -> System.out.println("world"),
4_000_000_000L);
final Task task3 = new Task(() ->
System.out.println("You should not see this text."),
100_000_000_000L);
scheduler.schedule(task1);
scheduler.schedule(task2);
scheduler.schedule(task3);
// This sleep command will continue after 7 seconds since the start of
// the ENTIRE program, so it halts for 3 seconds after task2 is carried:
try {
Thread.sleep(7000L);
} catch (final Exception ex) {
}
scheduler.close();
}
}
Critique request
As always, I am eager to hear about any constructive commentary.