Java Concurrency : Future
Tags: java, concurrency, multithreading
Published: 2023-09-12 02:30:00

In my previous post in this series, I talked about the concept of a _Callable_ in Java. We saw that when we execute a Callable task using the _ExecutorService_, we get back a `Future<T> `. What is this 'Future' ? In the previous post in this series, you have already seen basic usage of Futures. In this post, we'll discuss two of the most important methods detailed out in the Future API.

---

In simple words, a [_Future_](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html) is a placeholder for the result of an asynchronous computation that is to be performed some time in the future. It is used as a reference to the result of an asynchronous computation.

---

## T get()
This method can be used to 'get' the result of the asynchronous computation. The callable task can be in any of the many possible states when you invoke this method on the corresponding future. Three of the most basic possible scenarios is

- Still RUNNING
- Normal COMPLETION
- Exceptional COMPLETION

The following code snippet shows each of these scenarios in action. Notice how when our callable is still running, the .get() call blocks the main thread.

```
    public static void main(String[] args) {
        System.out.println("Main method has started.");

        ExecutorService executorService = Executors.newFixedThreadPool(3);

        Future<Integer> stillRunning = executorService.submit(new Task(2000, false));
        Future<Integer> normalCompletion = executorService.submit(new Task(20, false));
        Future<Integer> exceptionalCompletion = executorService.submit(new Task(20, true));

        try {
            long startEpoch = System.currentTimeMillis();
            Integer resultStillRunning = stillRunning.get();
            long endEpoch = System.currentTimeMillis();

            System.out.println("[1] Result : " + resultStillRunning);
            System.out.println("[1] Time Taken For .get() call : " + (endEpoch - startEpoch));
        } catch (InterruptedException e) {
            System.out.println("[1] InterruptedException");
        } catch (ExecutionException e) {
            System.out.println("[1] ExecutionException : " + e.getCause().getMessage());
        }


        try {
            long startEpoch = System.currentTimeMillis();
            Integer resultNormalCompletion = normalCompletion.get();
            long endEpoch = System.currentTimeMillis();

            System.out.println("[2] Result : " + resultNormalCompletion);
            System.out.println("[2] Time Taken For .get() call : " + (endEpoch - startEpoch));
        } catch (InterruptedException e) {
            System.out.println("[2] InterruptedException");
        } catch (ExecutionException e) {
            System.out.println("[2] ExecutionException : " + e.getCause().getMessage());
        }


        try {
            long startEpoch = System.currentTimeMillis();
            Integer resultExceptionalCompletion = exceptionalCompletion.get();
            long endEpoch = System.currentTimeMillis();

            System.out.println("[3] Result : " + resultExceptionalCompletion);
            System.out.println("[3] Time Taken For .get() call : " + (endEpoch - startEpoch));
        } catch (InterruptedException e) {
            System.out.println("[3] InterruptedException");
        } catch (ExecutionException e) {
            System.out.println("[3] ExecutionException : " + e.getCause().getMessage());
        }

        executorService.shutdown();

        System.out.println("Main method has finished.");
    }

    private static class Task implements Callable<Integer> {

        private final int sleepForMilliseconds;
        private final boolean throwException;

        public Task(int sleepForMilliseconds, boolean throwException) {
            this.sleepForMilliseconds = sleepForMilliseconds;
            this.throwException = throwException;
        }

        @Override
        public Integer call() throws Exception {
            if (throwException) {
                throw new Exception("Something bad happened...");
            }

            TimeUnit.MILLISECONDS.sleep(sleepForMilliseconds);
            return new Random().nextInt();
        }
    }
```
The output is
```
Main method has started.
[1] Result : 1548882964
[1] Time Taken For .get() call : 2005
[2] Result : 1474341827
[2] Time Taken For .get() call : 0
[3] ExecutionException : Something bad happened...
Main method has finished.
```

---

## boolean isDone()
This method returns true if this task 'completed'. Completion may be due to normal or exceptional. Also, while we will not be discussing cancelling of tasks in this post, the `isDone()` method returns true even when the task was cancelled.

The following code snippet shows this method in action.

```
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("Main method has started.");

        ExecutorService executorService = Executors.newFixedThreadPool(3);

        Future<Integer> future = executorService.submit(new Task());

        while (!future.isDone()) {
            System.out.println("Task Is Still Not Done!");
            Thread.sleep(6L); // busy-waiting (just for demonstration, bad idea in production code)
        }
        System.out.println("Task Is Done With Result : " + future.get());

        executorService.shutdown();

        System.out.println("Main method has finished.");
    }

    private static class Task implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            TimeUnit.MILLISECONDS.sleep(20);
            System.out.println("Task Completed!");
            return new Random().nextInt();
        }
    }
```
The output is

```
Main method has started.
Task Is Still Not Done!
Task Is Still Not Done!
Task Is Still Not Done!
Task Is Still Not Done!
Task Completed!
Task Is Done With Result : 1549778552
Main method has finished.
```