javamultithreadinggoogle-guava-cache

What's the best way to wait on a scheduled future that has 0 delay?


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.


Solution

  • 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();