javafutureexecutorservicecompletable-futurejava.util.concurrent

Why executor service submitted task is not executing the task async?


I'm trying to play with async java in my intellij local. My intention is that i will call calucalateAsync() method from main method and then put the debug point on System.out.println("calculate async was called");.

Ideally the submitted task to executors service should be executed concurrently, but the issue is until the execution of the main method is being done, the task is never being executed. Please let me know

Please find below the code:

CompletableFuturePlay.java

public class CompletableFuturePlay {

public void calculateAsync() throws InterruptedException {
    System.out.println("inside calculate async method");
    Executors.newFixedThreadPool(10).submit(() -> {
        System.out.println("inside the submitted task");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("completed the thread");
        return;
    });
    
    System.out.println("outside executores block");
    
}}

PlayGround.java

public class PlayGround {

public static void main(String[] args) throws InterruptedException, ExecutionException {
    CompletableFuturePlay completableFuturePlay = new CompletableFuturePlay();
    completableFuturePlay.calculateAsync();
    System.out.println("calculate async was called");
}}

Output I'm seeing in terminal: enter image description here


Solution

  • Breakpoints can suspend:

    By default, in IntelliJ Idea, a breakpoint will suspend all threads.

    If you launch main (that will run calculateAsync on a separate thread) and you set a breakpoint on the line coming just after the call to calculateAsync that suspends all threads, what will happen is:

    1. Randomly, the beginning of calculateAsync may appear before you hit the breakpoint. However, this is not very likely (the thread has to fork, which takes "some time", so it's more likely that you hit the main breakpoint before the thread actually has the time to start.
    2. The main breakpoint is hit, so all threads will stop (here including the async thread that is running calculateAsync)
    3. Hence, because of the Thread.sleep within the second thread, you will never see the end of it until when the breakpoint is not released.

    The "easiest" option to observe the wished behavior is to set the breakpoint to suspend current thread only. If you do that, the secondary thread will be free to run even after you hit your breakpoint and so you will see what you wish to see.

    However, this is not a solid way to setup a multi-threading test. My suggestion is to use a CountDownLatch as follows (p.s. I'm writing the code directly on StackOverflow so I may be doing some mistake, please use some tolerance when actually running it):

    public class CompletableFuturePlay {
    
    public void calculateAsync(CountDownLatch latch, ExecutorService executor) throws InterruptedException {
        System.out.println("Scheduling asynchronous execution");
        executor.submit(() -> {
            System.out.println("Asynchrounous execution started");
            // do whatever consuming job here. You can mock it with a Thread.sleep if you want
            System.out.println("Asynchronous execution ended");
            latch.countDown(); //send a signal to the latch that your job is over
            return;
        });
        
        System.out.println("Scheduled asynchronous execution");
        
    }}
    

    ... and then the usage:

    public class PlayGround {
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        CountDownLatch latch = new CountDownLatch(1); //<-- only 1 is enough
        CompletableFuturePlay completableFuturePlay = new CompletableFuturePlay();
        completableFuturePlay.calculateAsync(latch, executor);
        System.out.println("calculate async was called");
        latch.await(1, Time.MINUTES); //<-- ask the main thread to wait for the other thread to complete the execution - timeout after 1 minute
        System.out.println("resume execution of main thread");
    }}