Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 433
Nathan Feger Avatar asked Jul 07 '10 17:07

Nathan Feger


1 Answers

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.

like image 185
Andrzej Doyle Avatar answered Oct 21 '22 15:10

Andrzej Doyle