1

I am exploring multi threading in Java. I have come across this program to compute primes. Primes are computed in the most inefficient way possible. This is done in purpose to maximize the execution time.

Here is the code

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.*;

import static java.lang.System.exit;

public class Part19_CalculatePrimeNumbersUsingFutureAndCallable {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        List<Future<Integer>> futures = new ArrayList<>();
        System.out.println(String.format("The main thread : %s", Thread.currentThread().getName()));
        while (true) {
            Scanner sc = new Scanner(System.in);
            System.out.println("\nI can tell you the nth prime number, Enter n(0 to exit): ");
            int n = sc.nextInt();
            if (n == 0)
                break;

            Callable<Integer> primeCalculator = new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    return calculatePrime(n);
                }
            };

            Future<Integer> primeNumberFuture = executorService.submit(primeCalculator);

            futures.add(primeNumberFuture);

        }

        System.out.println("EXITED");

        // check if any of the futures are completed

        futures.forEach(f -> {
            try {
                System.out.println(String.format("\tComputed prime %s", f.get()));
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        });

    }

    private static int calculatePrime(int n) {
        int num = 1, count = 0, i;

        while (count < n) {
            num = num + 1;
            for (i = 2; i <= num; i++) {
                // determines the modulo and compare it with 0
                if (num % i == 0) {
                    // breaks the loop if the above condition returns true
                    break;
                }
            }
            if (i == num) {
                // increments the count variable by 1 if the number is prime
                count = count + 1;
            }
        }
        return num;
    }
}

OUTPUT 1

The main thread : main

I can tell you the nth prime number, Enter n(0 to exit): 

0

EXITED

Process finished with exit code 0

This outputs as expected

OUTPUT 2

The main thread : main

I can tell you the nth prime number, Enter n(0 to exit): 
1

I can tell you the nth prime number, Enter n(0 to exit): 
2

I can tell you the nth prime number, Enter n(0 to exit): 
3

I can tell you the nth prime number, Enter n(0 to exit): 
0
EXITED
  Computed prime 2
  Computed prime 3
  Computed prime 5

Here the program does not seem to terminate and I cannot understand why.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
H-Bar
  • 51
  • 4
  • 1
    [How to debug small programs](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) – Filburt Apr 27 '23 at 11:06
  • https://stackoverflow.com/questions/36644043/how-to-properly-shutdown-java-executorservice – tgkprog Apr 27 '23 at 11:23
  • with java>=9, you can squash the "whole body" into 1 line: `futures.add(executorService.submit(() -> calculatePrime(n)));` ;) – xerx593 Apr 27 '23 at 11:44

1 Answers1

2

Minimal fix

Add a:

executorService.shutdown(); // alternatively try(executorService.awaitTermination(...)) ...

as the last line of your main().

The executor service (seems to, surely implemented also "as a thread") "reserves resources" and prevents the main thread from exiting.

In the 0 scenario we don't submit any tasks...

Alternatives/Documentation (17):

shutdown() Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted...

shutdownNow() Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution. This method does not wait for actively executing tasks to terminate. Use awaitTermination to do that...

boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first...

  • System.exit(/*code*/); ;(

Thx, @tgkprog:

xerx593
  • 12,237
  • 5
  • 33
  • 64
  • 1
    In a batch program we generally dont want the threaad pool to exit so that is the default behaviour. Like xerx says add an await and then exit close the service. once all non daemon threads are done, the JVM will exit. Alternatively or additionally you can add a 10 or more second delay and call System.exit yourself to force the jvm to exit even if any task is still procesing or hung. – tgkprog Apr 27 '23 at 11:20
  • 1
    Can read https://www.google.com/search?q=java+daemon+vs+non+daemon+threads * https://stackoverflow.com/questions/45048346/how-to-exit-executorservice-after-all-the-threads-have-done-their-execution * https://stackoverflow.com/questions/36644043/how-to-properly-shutdown-java-executorservice – tgkprog Apr 27 '23 at 11:21
  • 1
    Actually, the executor service does _not_ prevent the main the main thread from exiting. If you are familiar with C or C++ then you probably know that the standard libraries for programs in those langauges call `exit()` after `main()` returns. That's not how Java works. A Java process does not exit until all\* of its threads have terminaed. – Solomon Slow Apr 27 '23 at 12:32
  • \* I'm ignoring the possibility of [_daemon threads_](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Thread.html#setDaemon(boolean)) for simplicity's sake. – Solomon Slow Apr 27 '23 at 12:35
  • i agree with all, @SolomonSlow except with "executor service does not prevent the main thread from exiting" because it is exactly/the only "thread to terminate" (which prevents exit) – xerx593 Apr 27 '23 at 12:57
  • 1
    But, the worker thread(s) that keep the process alive are not the _main_ thread. The main thread ends after `main` returns. – Solomon Slow Apr 27 '23 at 13:37