12

Can you think about any reason why this code doesn't work and always outputs "finished", but the second example works without any problems. I'm using latest JDK (8u45).

public static class MyClass implements Runnable {

        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {
                System.out.println("Interrupted");
                return;
            }
            System.out.println("Finished");
        }

        public static void main(String[] args) {
             // spot the difference ->
             ExecutorService executorService = Executors.newWorkStealingPool(4);
             Future future = executorService.submit(new MyClass());
             Thread.sleep(100);
             future.cancel(true);
        }
    }

And the following example works flawlessly:

public static class MyClass implements Runnable {

        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {
                System.out.println("Interrupted");
                return;
            }
            System.out.println("Finished");
        }

        public static void main(String[] args) {
             ExecutorService executorService = Executors.newSingleThreadExecutor();
             Future future = executorService.submit(new MyClass());
             Thread.sleep(100);
             future.cancel(true);
        }
    }

EDIT: Added return and updated sleep times and another example.

1
  • Wow, that's troubling. On my system (Java 1.8.0_45-b14, Linux 3.2.0-4-amd64), it doesn't print anything. Just like you, I'm finding that only the ExecutorService returned by newWorkStealingPool has this problem. One of us should submit this as a bug.
    – VGR
    Commented Apr 28, 2015 at 0:18

2 Answers 2

10

It's simpler than I thought originally. The problem is that work-stealing-pool is internally using ForkJoinPool and ForkJoinTask doesn't support cancel(true) and therefore it's not possible to cancel task after the task is started.

See javadoc documentation (http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinTask.html):

   mayInterruptIfRunning - this value has no effect in the default implementation 
   because interrupts are not used to control cancellation.
2
  • How embarrassing; I simply wasn't waiting for the sleep(2000) to finish. Anyway, good find. This is useful to know.
    – VGR
    Commented Apr 28, 2015 at 10:43
  • In retrospect, this is more obvious since "single thread executor" would imply no need to fork another thread. I'd be curious what would happen if you used .newFixedThreadPool(1)
    – djangofan
    Commented Jul 30, 2017 at 21:06
2

There is no way to forcibly terminate a Thread in Java. (Twenty years ago, Java 1.0 tried to provide this, and it turned out to be unworkable; the methods which attempted to do it are deprecated with no replacement.)

You, as the author of the Runnable, are responsible for properly responding to an interrupt by cleanly terminating your own run method. In your case, you should have exited your run method in the catch-block, but you didn't; you let the method's logic continue past the catch-block. So even when the thread is interrupted, the run method's last statement is always executed.

1
  • My mistake. I've drafted this example just quickly as extract from a bit more complicated code and forgotten to add return. The core idea of my question is slightly different - see edit.
    – JiriS
    Commented Apr 27, 2015 at 21:09

Not the answer you're looking for? Browse other questions tagged or ask your own question.