Callable and Future

There are two ways to create threads: one is to inherit threads directly, and the other is to implement the Runnable interface. One drawback of both approaches is that you can’t get the results of the execution after the task is completed. 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(a);
}
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(a) throws Exception;
}
Copy the code

This is a generic interface, and the type returned by the call() function is the V type passed in.

The use of Callable

Typically used in conjunction with the ExecutorService, 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

The argument type in the first Submit method is Callable.

For now, Callable can be used in conjunction with ExecutorService. Details about how to use Callable will be described later.

In general, we use the first submit method and the third submit method, and the second submit method is rarely used.

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:

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled(a);
    boolean isDone(a);
    V get(a) throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}
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.

Future provides three functions:
  1. Judge whether the task is completed;
  2. Can interrupt the task;
  3. Obtain the task execution results.

Because a Future is just an interface, it cannot be used directly to create objects, hence the following FutureTask.

FutureTask

FutureTask implements the RunnableFuture interface, which is defined as follows:
public interface RunnableFuture<V> extends Runnable.Future<V> {  
    void run(a);  
}
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.

An example of FutureTask
public class MyCallable implements Callable<String> {  
    private long waitTime;   
    public MyCallable(int timeInMillis){   
        this.waitTime=timeInMillis;  
    }  
    @Override  
    public String call(a) throws Exception {  
        Thread.sleep(waitTime);  
        //return the thread name executing this callable task  
        returnThread.currentThread().getName(); }}Copy the code
public class FutureTaskExample {  
     public static void main(String[] args) {  
        MyCallable callable1 = new MyCallable(1000);                       // The task to be performed
        MyCallable callable2 = new MyCallable(2000);  

        FutureTask<String> futureTask1 = new FutureTask<String>(callable1);// Encapsulate the Callable written task into a FutureTask object scheduled by the executor
        FutureTask<String> futureTask2 = new FutureTask<String>(callable2);  

        ExecutorService executor = Executors.newFixedThreadPool(2);        // Create a thread pool and return to the ExecutorService instance
        executor.execute(futureTask1);  // Execute the task
        executor.execute(futureTask2);    

        while (true) {  
            try {  
                if(futureTask1.isDone() && futureTask2.isDone()){// Complete both tasks
                    System.out.println("Done");  
                    executor.shutdown();                          // Close thread pools and services
                    return;  
                }  

                if(! futureTask1.isDone()){// Task 1 is not complete, and will wait until the task is complete
                    System.out.println("FutureTask1 output="+futureTask1.get());  
                }  

                System.out.println("Waiting for FutureTask2 to complete");  
                String s = futureTask2.get(200L, TimeUnit.MILLISECONDS);  
                if(s ! =null){  
                    System.out.println("FutureTask2 output="+s); }}catch (InterruptedException | ExecutionException e) {  
                e.printStackTrace();  
            }catch(TimeoutException e){  
                //do nothing  }}}}Copy the code