views:

63

answers:

2

Why does Java's scheduleWithFixedDelay work with a Runnable but not a FutureTask wrapping a runnable?

This can be shown pretty easily with two different code samples:

ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
        executorService.scheduleWithFixedDelay(new FutureTask<Integer>(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                System.out.println("beep");
                return 1;
            }
        }), 1, 5, TimeUnit.SECONDS);

produces:

beep

But the application does not exit, it simply appears to wait.

but:

ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
    executorService.scheduleWithFixedDelay(new Runnable() {

        @Override
        public void run() {
            System.out.println("beep ");
        }
    }, 1, 5, TimeUnit.SECONDS);

produces:

beep beep beep beep beep

at 5 second intervals.

It seems like there is some kind of lock happening here that I cannot determine.

+2  A: 

You're not supposed to pass FutureTask objects into a ScheduledExecutorService, you're supposed to pass in Runnable or Callable, which then returns you a FutureTask. The fact that FutureTask implements the Runnable interface seems to be an unfortunate design flaw, allowing you pass the output back in as input.

As to why you get this behaviour, it's possibly some weird interaction between the components, and probably undefined.

skaffman
+6  A: 

Because you're kind of abusing FutureTask.

A FutureTask is "a cancellable asynchronous computation" according to the Javadocs, but more colloquially it wraps a particular execution of Runnable/Callable to provide the asynchronicity. I didn't actually realise that it implemented Runnable until I checked just now - the implementation of run() "sets this Future to the result of its computation".

So what's happening in your first example is that you're scheduling the future task, its run method gets called after 1 second and so it calculates the result of the computation (i.e. runs the embedded Runnable). When this method exits, the FutureTask has now run and has its concrete result - so future invocations of run() are no-ops.

I think the root problem here is that it doesn't seem to make sense to schedule a FutureTask directly, at least not in the manner you're doing here. If you want a bit of code to run every five seconds, then you should definitely take the second approach. The FutureTask embodies a (single!) calculation; there's no reason why you'd want it to be invoked multiple times and in fact it's specifically caching the result to prevent this.

Andrzej Doyle
That's a great answer thanks, I was using a FutureTask with a runnable inside in this example, but the business case, was I was trying to jam a Callable<Integer> into the scheduler, and the FutureTask could take a callable as an argument, and satisfy the runnable interface.
Nathan Feger