DEV Community

Cover image for Java Concurrency : Callable
Shreyans
Shreyans

Posted on

Java Concurrency : Callable

In the last post in this series, we understood what a Runnable is in Java. Some of you may have noticed a big limitation of the Runnable API, that it does not return a value. This means that while you can use a Runnable to execute some unit of work in a separate thread, there isn't a straightforward way for you to retrieve a meaningful value which may represent the result of that work.

For the reader's understanding, the code below shows one way how you might retrieve the result of a Runnable. However, please note that this is just for your understanding and you will not find code like this in production grade applications.

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

        BlockingQueue<String> resultsHolder = new ArrayBlockingQueue<String>(10);

        new Task(resultsHolder);
        new Task(resultsHolder);
        new Task(resultsHolder);

        try {
            System.out.println(resultsHolder.take());
            System.out.println(resultsHolder.take());
            System.out.println(resultsHolder.take());
        } catch (InterruptedException e) {
            // thrown when current thread was interrupted while waiting on the blocking call .take()
            throw new RuntimeException(e);
        }

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

    private static class Task extends Thread {

        private static int numTasks;
        private final int id;
        private final BlockingQueue<String> resultsHolder;

        public Task(BlockingQueue<String> resultsHolder) {
            this.id = ++numTasks;
            this.resultsHolder = resultsHolder;
            this.start();
        }

        @Override
        public void run() {
            System.out.println("Task has started.");

            try {
                TimeUnit.MILLISECONDS.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            String result = "Hello, World! This is Task : " + id;
            this.resultsHolder.add(result);

            System.out.println("Task has finished.");
        }
    }
Enter fullscreen mode Exit fullscreen mode

Java Concurrency Framework provides the Callable interface. Along with certain other differences and advantages (more about this in another post), It solves the above mentioned limitation of Runnable.

To execute a Callable task, you can use an ExecutorService to get a Future representing the result of the task. (More about Future and ExecutorService in another post)

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

        Task t1 = new Task();
        Task t2 = new Task();
        Task t3 = new Task();

        ExecutorService executorService = Executors.newFixedThreadPool(3);

        Future<String> f1 = executorService.submit(t1);
        Future<String> f2 = executorService.submit(t2);
        Future<String> f3 = executorService.submit(t3);

        try {
            System.out.println(f1.get());
            System.out.println(f2.get());
            System.out.println(f3.get());
        } catch (InterruptedException e) {
            // thrown when current thread was interrupted
            // while waiting on the blocking call .get()
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            // thrown when attempting to retrieve the result
            // of a task that aborted by throwing an exception.
            throw new RuntimeException(e);
        }

        executorService.shutdown();

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

    private static class Task implements Callable<String> {

        private static int numTasks;
        private final int id;

        public Task() {
            this.id = ++numTasks;
        }

        @Override
        public String call() {
            System.out.println("Task has started.");

            try {
                TimeUnit.MILLISECONDS.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            return "Hello, World! This is Task : " + id;
        }
    }
Enter fullscreen mode Exit fullscreen mode

The output is

Main method has started.
Task has started.
Task has started.
Task has started.
Hello, World! This is Task : 1
Hello, World! This is Task : 2
Hello, World! This is Task : 3
Main method has finished.
Enter fullscreen mode Exit fullscreen mode

In addition to Callables, An ExecutorService can also be used for executing Runnables.

The ExecutorService provides these methods for this purpose. Do check these out!

Top comments (0)