The Runnable problem

You are already familiar with the Runnable interface and the Thread class that implements it. Let's recall what this interface looks like:


public interface Runnable {
	public abstract void run();
}

Note that the run method's return type is void. But what if we need our thread to return some result of its work in the form of a number, a string, or some other object? Then we must come up with a workaround. Something like this:


public class Fibonacci implements Runnable {
 
 
 
	private final int index;
 
	private int result;
 
 
 
	public Fibonacci(int index) {
 
    		this.index = index;
 
	}
 
 
 
	@Override
 
	public void run() {
 
    		int first = 0;
 
    		int second = 1;
 
    		if (index == 1) {
 
        			result = first;
 
    		} else if (index == 2) {
 
        			result = second;
 
    		} else {
 
        			for (int i = 0; i < index - 2; i++) {
 
            				int temp = second;
 
            				second += first;
 
            				first = temp;
 
        			}
 
 
 
            			result = second;
 
    		}
 
	}
 
 
 
	public static void printByIndex(int index) throws InterruptedException {
 
    		Fibonacci fibonacci = new Fibonacci(index);
 
    		Thread thread = new Thread(fibonacci);
 
    		thread.start();
 
    		thread.join();
 
    		System.out.println("Fibonacci number " + index + ": " + fibonacci.result);
 
	}
 
}

Let's run the following main method:


	public static void main(String[] args) throws Exception {
    		Fibonacci.printByIndex(10);
	}

The console will display:

Fibonacci number 10: 34

This code has several drawbacks. For example, as a result of the call to the join method, the main thread will block while the printByIndex method is executed.

Callable interface

Now let's look at the interface that Java provides us out of the box, which can be used as an alternative to Runnable. This is the Callable interface:


public interface Callable<V> {
 
	V call() throws Exception;
 
}

As you can see, just like Runnable, it has only one method. This method serves the same purpose as the run method — it contains the code that will be executed in a parallel thread. As for differences, take a look at the return value. Now it can be any type that you specify when implementing the interface:


public class CurrentDate implements Callable<Long> {
 
	@Override
 
	public Long call() {
 
    		return new Date().getTime();
 
	}
 
}

Another example:


Callable<String> task = () -> {
 
	Thread.sleep(100);
 
	return "Done";
 
};

Here's something else useful — the call method can throw an Exception. That means that, unlike the run method, in the call method we don't have to handle the checked exceptions that occur inside the method:


public class Sleep implements Runnable {

	@Override

	public void run() {

    	    try {

        	        Thread.sleep(1000);

    	    } catch (InterruptedException ignored) {

    	    }

	}

}

public class Sleep implements Callable {

	@Override

	public Object call() throws InterruptedException {

    	    Thread.sleep(1000);

    	    return null;

	}

}

Future interface

Another interface that works closely with Callable is Future. Future represents the result of asynchronous (parallel) computations (the value returned by the call method). It lets you check whether the calculations are done, wait for the calculations to finish, get the result of the calculations, and more.

Methods of the Future interface

  • boolean isDone() — this method returns true if this task (computation) is done. Tasks that ended normally, ended with an exception, or were canceled are considered done.

  • V get() — if necessary, this method blocks the thread that called it, and returns the result of the calculations when they are done.

  • V get(long timeout, TimeUnit unit) — like the previous method, this method blocks the thread that called it, waiting for the result, but only for the time specified by the method parameters.

  • boolean cancel(boolean mayInterruptIfRunning) — this method tries to stop the execution of the task. If the task has not started running yet, it will never run. If the task was in progress, then the mayInterruptIfRunning parameter determines whether an attempt will be made to interrupt the thread executing the task. After the cancel method is called, the isDone method will always return true.

  • boolean isCancelled() — this method returns true if the task is canceled before it finishes normally. The method will always return true if the cancel method was previously called and returned true.

Example of code using Callable and Future


import java.util.HashMap;
 
import java.util.Map;
 
import java.util.concurrent.*;
 
 
 
public class Fibonacci implements Callable<Integer> {
 
 
 
	private final int index;
 
 
 
	public Fibonacci(int index) {
 
    		this.index = index;
 
	}
 
 
 
	@Override
 
	public Integer call() {
 
    		int first = 0;
 
    		int second = 1;
 
    		if (index == 1) {
 
        			return first;
 
    		} else if (index == 2) {
 
        			return second;
 
    		} else {
 
        		for (int i = 0; i < index - 2; i++) {
 
            			int temp = second;
 
            			second += first;
 
            			first = temp;
 
        		}
 
 
 
        			return second;
 
    		}
 
	}
 
 
 
	public static Future<Integer> calculateAsync(int index) throws Exception {
 
    		Fibonacci fibonacci = new Fibonacci(index);
 
 
 
    		// The future object will represent the result of running the fibonacci task.
 
    		FutureTask<Integer> future = new FutureTask<>(fibonacci);
 
 
 
    		// Because the FutureTask class implements both the Future interface and the Runnable interface,
 
	 	// you can pass instances of it to the Thread constructor
 
    		Thread thread = new Thread(future);
 
    		thread.start();
 
 
 
    		return future;
 
	}
 
}

Let's run the following main method:


	public static void main(String[] args) throws Exception {
    		Map<Integer, Future<Integer>> tasks = new HashMap<>();
    		for (int i = 10; i < 20; i++) {
        			tasks.put(i, Fibonacci.calculateAsync(i));
    		}
 
    		for (Map.Entry<Integer, Future<Integer>> entry : tasks.entrySet()) {
        			Future<Integer> task = entry.getValue();
        			int index = entry.getKey();
        			int result;
        			// Check whether the task is done
        			if (task.isDone()) {
            				// Get the result of the calculations
            				result = task.get();
        			} else {
            				try {
                				// Wait another 100 milliseconds for the result of the calculations
                				result = task.get(100, TimeUnit.MILLISECONDS);
            				} catch (TimeoutException e) {
                				// Interrupt the task
                				task.cancel(true);
                				System.out.println("Fibonacci number " + index + " could not be calculated in the allotted time.");
                				return;
            				}
        			}
        			System.out.println("Fibonacci number " + index + ": " + result);
    		}
	}

The console will display:

Fibonacci number 16: 610
Fibonacci number 17: 987
Fibonacci number 18: 1597
Fibonacci number 19: 2584
Fibonacci number 10: 34
Fibonacci number 11: 55
Fibonacci number 12: 89
Fibonacci number 13: 144
Fibonacci number 14: 233
Fibonacci number 15: 377