Prior to Java 1.5, there were two ways to create threads: one was to inherit threads directly or the other was to implement the Runnable interface. No matter how we implement multithreading, we need to call the start method in Thread class to request IO, CUP and other resources to the operating system. Because the thread run method does not return a value, if you want to get results, you have to share variables or use thread communication to get results, which can be cumbersome to use.

Since Java 1.5, Callable and Future have been available to get the result of a task’s execution after it has finished.

Introduction to Callable and Future

The Callable interface represents a piece of code that can be called and return results; The Future interface represents asynchronous tasks that are Future results of tasks that have not yet been completed. So Callable is used to produce results and Future is used to get results.

The Callable interface uses generics to define its return type. The Executors class provides some useful methods for executing tasks in the Callable thread pool. Since the Callable task is parallel (parallel is when the whole thing looks parallel, but only one thread is executing at any point in time), we have to wait for the result it returns. Java. Util. Concurrent. The Future object to solve the problem for us. A Future object is returned after a Callable task is submitted by the thread pool. It can be used to know the status of the Callable task and get the execution result returned by the Callable. The Future provides a get() method that lets us wait for the Callable to end and get its execution result.

Callable and Runnable

Java.lang.Runnable is an interface in which only one run() method is declared:

public interface Runnable {

    public abstract void run();

}Copy the code

Because the run() method returns a void value, no results can be returned after the task is executed.

Callable, located under the java.util.concurrent package, is also an interface in which only one method is declared, but this method is called Call () :

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}Copy the code

As you can see, this is a generic interface, and the type returned by the call() function is the V type passed in.

So how do you use Callable?

It is typically used in conjunction with the ExecutorService, and several overloaded versions of the Submit method are declared in the ExecutorService interface:

<T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<? > submit(Runnable task);Copy the code

Future

A Future is a Future that cancels, queries, and retrieves the results of a specific Runnable or Callable task. If necessary, the results of the execution can be obtained through the GET method, which blocks until the task returns results.

The Future class, located under the java.util.Concurrent package, is an interface:

<T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<? > submit(Runnable task);Copy the code

There are five methods declared in the Future interface, and the purpose of each method is explained below:

The cancel method is used to cancel a task, returning true on success or false on failure. The mayInterruptIfRunning parameter indicates whether ongoing tasks that have not completed are allowed to be canceled, and true indicates that ongoing tasks can be canceled. Whether mayInterruptIfRunning is true or false, this method must return false if the task is completed, or if you cancel the completed task. True if the task is executing and false if mayInterruptIfRunning is set to true. If the task has not yet been executed, true is always returned regardless of whether mayInterruptIfRunning is true or false.

The isCancelled method indicates whether the task was cancelled successfully or returns true if the task was cancelled before it completed normally.

The isDone method indicates whether the task is complete, and returns true if it is.

The get() method is used to get the result of the execution, which blocks and does not return until the task is finished.

Get (long timeout, TimeUnit Unit) is used to obtain the execution result. If no result is obtained within the specified time, null is returned.

In other words, Future provides three functions:

1) Judge whether the task is completed;

2) Can interrupt the task;

3) Can obtain the task execution results.

Future is used to represent the result of asynchronous computation. Its implementation class has Java. Util. Concurrent, FutureTask < V > and javax.mail swing. The SwingWorker < T, V >, if you don’t want to branch on the Android platform thread blocking the main thread, and want to achieve branch thread of execution as a result, You can use FutureTask.

FutureTask

Java class is a single inheritance design, if the use of Thread inheritance to achieve multithreading, it can not inherit other classes, the use of interface can better achieve data sharing

FutureTask implements the RunnableFuture interface, which is defined as follows:

public interface RunnableFuture<V> extends Runnable, Future<V> {  
    void run();  
} Copy the code

As you can see, this interface implements the Runnable and Future interfaces, and the implementation of the interface is implemented by FutureTask. The class has two constructors:

public FutureTask(Callable<V> callable) {  
        if (callable == null) 
            throw new NullPointerException();  
        sync = new Sync(callable);  
}  
    
public FutureTask(Runnable runnable, V result) {  
	sync = new Sync(Executors.callable(runnable, result));  
}Copy the code

Two constructors are provided, one taking Callable and one taking Runnable. The associations between these classes are very flexible for task modeling, allowing you to write a task as a Callable based on the Runnable feature of FutureTask (because it implements the Runnable interface) and encapsulate it into a FutureTask that can be scheduled by the implementer and cancelled if necessary.

FutureTask can be scheduled by the executor, which is critical. The methods it provides are basically a combination of Future and Runnable interfaces: Get (), cancel, isDone(), isCancelled(), and Run (), and the run() method is usually called by the executor, so we don’t need to call it directly. The FutureTask class also implements the Runnable interface, so you can submit to Thread and Executors directly:

public class CallableAndFuture {  
        public static void main(String[] args) {  
            Callable<Integer> callable = new Callable<Integer>() {  
                public Integer call() throws Exception {  
                    returnnew Random().nextInt(100); }}; FutureTask<Integer> future = new FutureTask<Integer>(callable); new Thread(future).start(); try { Thread.sleep(5000); Int result = future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }}}Copy the code

public class CallableAndFuture {  
    public static void main(String[] args) { 
 
        //ExecutorService.submit()
        ExecutorService threadPool = Executors.newSingleThreadExecutor();  
        Future<Integer> future = threadPool.submit(new Callable<Integer>() {  
            public Integer call() throws Exception {  
                returnnew Random().nextInt(100); }}); try { Thread.sleep(5000); Int result = future.get()); //Future.get() } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }}Copy the code

If you want to execute multiple tasks with return values and get multiple return values, you can use CompletionService:

The CompletionService is an Executor plus BlockingQueue. When a child thread has a series of concurrent tasks, the main thread needs to retrieve the return values of the child’s tasks in real time and process them sequentially, whoever returns them first.

public class CallableAndFuture {  
    public static void main(String[] args) {  
        ExecutorService threadPool = Executors.newCachedThreadPool();  
        CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);  
        for(int i = 1; i < 5; i++) {  
            final int taskID = i;  
            //CompletionService.submit()
            cs.submit(new Callable<Integer>() {  
                public Integer call() throws Exception {  
                    returntaskID; }}); } // Can do somethingfor(int i = 1; i < 5; i++) { try { int result = cs.take().get()); //CompletionService.take() returns Future} catch (InterruptedException e) {e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }}}}Copy the code

Or don’t use CompletionService: First create a collection of type Future, add it to the collection with the return value of the Executor submitted task, and finally facilitate the collection to retrieve the data.

The difference between:

For the Future collection method, submit tasks are not necessarily completed in the order they were added to the self-maintained list. Each Future object traversed through the list is not necessarily in a completed state, and calling the get() method blocks. If the system is designed so that each thread completes and continues to do something based on its results, this adds extra waiting time for threads that finish first but are behind the list.

The implementation of CompletionService maintains a BlockingQueue that holds the Future object. The Future will only be added to the Queue if its state is finished. The take() method is the Consumer of producer-consumer. It will pull the Future from the Queue, and if the Queue is empty, it will block there until a finished Future is added to the Queue.

Therefore, what is done first must be taken out first. This reduces unnecessary waiting time.