In particular, does it implement a while-true loop internally like so?
while (System.currentTimeMillis() < timeToRunTask){
Thread.sleep(1000);
}
doTask();
In particular, does it implement a while-true loop internally like so?
while (System.currentTimeMillis() < timeToRunTask){
Thread.sleep(1000);
}
doTask();
A Task will be decorated and put into the DelayedWorkQueue. Then it is ensured there is a Thread that tries to take the just added entry.
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
private void delayedExecute(RunnableScheduledFuture<?> task) {
if (isShutdown())
reject(task);
else {
// Enqueue Task vv
super.getQueue().add(task);
if (isShutdown() &&
!canRunInCurrentRunState(task.isPeriodic()) &&
remove(task))
task.cancel(false);
else
// Make sure, there is a worker to be blocked
ensurePrestart();
}
}
take()
will be called in the worker thread's run(). The actual delay is in here: (See comments)
public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
RunnableScheduledFuture<?> first = queue[0];
if (first == null)
// if queue is empty, wait (block) until signaled "not empty"
available.await();
else {
// compute how long Task still has to wait in NANOSECONDS
long delay = first.getDelay(NANOSECONDS);
// no further waiting? Then go on and execute!
if (delay <= 0)
return finishPoll(first);
first = null; // don't retain ref while waiting
if (leader != null) // leader/follower Pattern:
// see Comments in DeleyedWorkQueue
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
// ########## HERE IT COMES vvvvvvvvvvvvvvvvvvvvvvvvvvvv
available.awaitNanos(delay);
// Wait (block worker thread) for 'delay' nanos
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
This variant of the Leader-Follower pattern (http://www.cs.wustl.edu/~schmidt/POSA/POSA2/) serves to minimize unnecessary timed waiting. When a thread becomes the leader, it waits only for the next delay to elapse, but other threads await indefinitely. The leader thread must signal some other thread before returning from take() or poll(...), unless some other thread becomes leader in the interim. Whenever the head of the queue is replaced with a task with an earlier expiration time, the leader field is invalidated by being reset to null, and some waiting thread, but not necessarily the current leader, is signalled. So waiting threads must be prepared to acquire and lose leadership while waiting.
From comments on grepcode.
Mind that this is for OpenJDK. Oracle or other JDKs might differ in implementation.
You can read about Condition.awaitNanos
by following the link.