3

IDE: IntelliJ
JDK: Java 11

While was testing a sample code from lecture, I have found something really weird that my program just won't stop, even though there aren't any loop!

import java.util.concurrent.Callable;

public class FindMaxTask implements Callable<Integer> {
    private int[] data;
    private int start;
    private int end;

    public FindMaxTask(int[] data, int start, int end) {
        this.data = data;
        this.start = start;
        this.end = end;
    }

    public Integer call() {
        int max = Integer.MIN_VALUE;
        for (int i = start; i < end; i++) {
            if (data[i] > max) {
                max = data[i];
            }
        }
        return max;
    }
}

This is a FindMaxTask that implements Callable interface which performs a finding maximum value in given range of array.

public static void testCallable() throws ExecutionException, InterruptedException {
        final int dataSize = 10000;
        int[] data = new int[dataSize];
        Random random = new Random();

        for (int i = 0; i < dataSize; i++) {
            data[i] = random.nextInt(1000000);
        }

        FindMaxTask task0 = new FindMaxTask(data, 0, dataSize / 2);
        FindMaxTask task1 = new FindMaxTask(data, dataSize / 2, dataSize);

        ExecutorService service = Executors.newFixedThreadPool(2);

        Future<Integer> future0 = service.submit(task0);
        Future<Integer> future1 = service.submit(task1);

        int ret0 = future0.get();
        int ret1 = future1.get();

        System.out.println("ret0: " + ret0 + System.lineSeparator() + "ret1: " + ret1);
    }

This is a testCallable static function in Main class.

If I run testCallable function in main function, program just stops after printing each ret value in console.

Is this problem has something to do with Callable or Future? (I have tried debugging future1.isDone() with value of true after printing ret0,ret1 so it clearlly doesn't seems like that chlid thread has been blocked)
Please give me an advise why is this happening

co_lin
  • 125
  • 7
  • 1
    You have to shut down the thread pool. Otherwise, you still have non-daemon threads running in the background. The JVM doesn't exit until all non-daemon threads have stopped. – David Conrad Sep 23 '21 at 15:04
  • @DavidConrad I thought child threads will vanish after main thread is exited, is this a special feature of JVM? – co_lin Sep 23 '21 at 15:08
  • Child threads only vanish after main thread is exited if they are daemon threads. This is true since Java 1.0. See [`Thread`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html). – David Conrad Sep 23 '21 at 15:10
  • 2
    [`Executors.newFixedThreadPool`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool-int-) is documented as saying that "The threads in the pool will exist until it is explicitly shutdown.". – David Conrad Sep 23 '21 at 15:12

1 Answers1

4

You have to shut down the thread pool. Otherwise, you still have non-daemon threads running in the background. The JVM doesn't exit until all non-daemon threads have stopped.

ExecutorService service = Executors.newFixedThreadPool(2);
try {
    // do things with the executor service
} finally {
    service.shutdown();
}

If you want to block until all tasks have completed, you can use awaitTermination, or call shutdown and then loop until isTerminated returns true. If you want to try to terminate any running or queued tasks immediately, you can use shutdownNow.

David Conrad
  • 15,432
  • 2
  • 42
  • 54
  • 1
    If tasks were runnable then it would be a good idea to use `service.awaitTermination(..)` – the Hutt Sep 23 '21 at 15:18
  • @onkarruikar There's no need. The main method isn't going to do any more work that depends on the tasks having completed, and besides, the code is already calling get() on the futures it created. But if for some reason you needed it to block until the tasks were completed, you could use awaitTermination. – David Conrad Sep 23 '21 at 15:24
  • 1
    @DavidConrad Since there is no downside to using `awaitTermination`, I suggest teaching its use as a good habit. Deserves at least a mention in your good Answer. – Basil Bourque Sep 23 '21 at 15:25
  • 2
    And, by the way, if [*Project Loom*](https://wiki.openjdk.java.net/display/loom/Main) succeeds, this code becomes a bit simpler as `ExecutorService` will be [`AutoCloseable`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/AutoCloseable.html) for use in [try-with-resources](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) syntax. Experimental builds available now based on early-access Java 18. – Basil Bourque Sep 23 '21 at 15:27
  • @BasilBourque I will add a mention of it, but I don't see how it's a good habit. If you don't want to block until all the tasks are completed, there is no reason to. I wonder if there is a misconception here that `shutdown` somehow kills running tasks, or prevents queued tasks from executing? It does not. – David Conrad Sep 23 '21 at 16:36