Java concurrent programming summary 11: Future, FutureTask

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


public class FutureTask<V> implements RunnableFuture<V> {
 
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        // ...       
    }
 
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        // ...
    }
}

As you can see from the constructor, FutureTask can either wrap Callable tasks or Runnable tasks, but in the end it will always convert Runnable into Callable tasks, which is actually an adaptation process.

Through the above analysis, we can see that the entire Future actually consists of three core components:

  1. Real task/data type (usually task execution is slow, or data construction takes a long time);
  2. Future interface (the caller uses the credential to obtain the result of the real task/data), that is, the Future interface.
  3. Future implementation class (used to wrap real tasks/data), namely FutureTask implementation class.

In the Future mode provided by JUC, the most important is the FutureTask class. FutureTask was introduced with JUC when JDK1.5. It represents an asynchronous task. This task is generally submitted to Executor for execution, and of course it can also be called The party directly calls the run method to run.

In the book "The Art of Java Concurrent Programming", the author divides FutureTask into 3 states according to the timing of the execution of the FutureTask.run() method :

  1. Not started . Before the FutureTask.run() method is executed, the FutureTask is in an unstarted state . When a FutureTask is created, the FutureTask is not started before the FutureTask.run() method is executed.
  2. Has been activated . During the execution of the FutureTask.run() method, the FutureTask is in the started state.
  3. Completed . The FutureTask.run() method execution ends, or the FutureTask.cancel(...) method is called to cancel the task, or an exception is thrown during the execution of the task. These situations are called the completed state of the FutureTask.

The following figure summarizes the state change process of FutureTask:

Because FutureTask has these three states, the results of executing the get method and cancel method of FutureTask are also very different in different states. Here is a summary of the get method and the cancel method:

get method:

  • When the FutureTask is not started or started , the execution of the FutureTask.get() method will cause the calling thread to block .
  • If the FutureTask is in the completed state , calling the FutureTask.get() method will cause the calling thread to return the result immediately or throw an exception .

cancel method:

  • When FutureTask is not started , execute FutureTask.cancel() method and this task will never be executed;
  • When FutureTask in started state , executing FutureTask.cancel (true) method to interrupt the thread way to stop the task to continue, if you do FutureTask.cancel (false) will not have any impact on the thread that is performing a task;
  • When the FutureTask is in the completed state , executing the FutureTask.cancel(...) method will return false .

Use the following figure to summarize the get() method and cancel() method of Future:

In addition to the Future interface, FutureTask also implements the Runnable interface.

Therefore, FutureTask can be handed over to Executor for execution, or it can be directly executed by the calling thread (FutureTask.run()).

In addition, the acquisition of FutureTask can also return a FutureTask object through the ExecutorService.submit() method, and then through the FutureTask.get() or FutureTask.cancel method.

Application scenario: When a thread needs to wait for another thread to execute a task before it can continue to execute, FutureTask can be used at this time. Suppose there are multiple threads executing several tasks, and each task can only be executed once at most. When multiple threads try to perform the same task, only one thread is allowed to perform the task, and other threads need to wait for the task to be executed before continuing.

 

 

Reference: You listen to _ Great God, "The Art of Java Concurrent Programming"

 

Guess you like

Origin blog.csdn.net/ScorpC/article/details/113915682