I am trying to avoid sleeping the current thread until a ScheduledFuture executes with a 0 delay. Unfortunately, I can't find a hook against the future that informs when the runnable executes. The future in question wraps a guava cache.put(key,value) operation. The runnable should be called in advance of the cache expiring the key...essentially, I want one key to never expire.
final Runnable refresh = new Runnable() {
@Override
public void run()
{
cache.put( key, value );
}
};
// replace the token when 95% of the ttl has passed
long refreshInterval = (long)( keyExpires * 1000 *
0.5 );
// execute the future task and then delay the current thread long enough for the
// executor to process the runnable. 50 ms should be long enough.
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate( refresh,
0,
refreshInterval,
TimeUnit.MILLISECONDS );
/// this is the code I'd like to avoid
try {
Thread.sleep( 50 );
} catch( InterruptedException e1 ) {} catch( ExecutionException e ) {
// TODO Auto-generated catch block
e.printStackTrace();
}
The executor service does run the code immediately but there's lag time to spin up a thread. That lag will be system specific, so I want to avoid an arbitrary sleep.
I am using ScheduledThreadPoolExecutor to create the ScheduledFuture and I can get the behaviour I want using an accessor of that type, like isDone(). However, that seems hacky too. Is there a cleaner implementation that offers the behaviour of sleeping the current thread without using a side effect of the Executor service?
Thanks, Robin
Edit: to show the test that fails without a Thread.sleep()
cache.putNonExpiring( "key", "value" );
Assert.assertNotNull( "Immediate get should have value", cache.get( "key" ) );
To work correctly, a put(key,value) should be performed synchronously to allow an immediate get(key) operation.
Perhaps you could use a semaphore or other synchronization type that the current thread blocks on until the refresh runnable releases the semaphore
// A semaphore initialized with no permits
final Semaphore runnableExecuting = new Sempahore(0);
final Runnable refresh = new Runnable()
{
@Override
public void run()
{
// Release one permit. This should unblock the thread
// scheduled this task. After the initial releasing
// the semaphore is essentially unneeded
runnableExecuting.release();
// Your code
}
}
// After executor scheduling
// Attempt to acquire a permit, which the semphore initially has none.
// This will block until a permit becomes available
runnableExecuting.acquire();