The Future is introduced
For parallel task requirements, some people think through multi-threading implementation can directly, but for multithreading, we will consider the execution order, while it is running will also need to receive the result of the execution returns, if wait for multiple parallel after completion of task execution, also need to merge the results of task, there are three kinds of the realizing methods of multithreading: Inherit Thread class, implement Runnable interface and implement Callable interface. During execution, we will consider the execution sequence of tasks. We need to wait for the completion of multiple tasks and then merge the results, which will lead to the following problems:
- To control the execution order of threads, join() is used to wait for the end of threads. However, join() will cause blocking call, affecting the performance of execution and failing to improve the parallel execution rate. However, wait(),notify() and notifyAll() can also be used. The realization of the process is more complex, the development of the entry conditions are relatively high, more problems to consider, easy to produce bugs
- After the thread execution is completed, the execution result of each task needs to be collected. Due to the data security between threads, we will use shared variables or communication between threads to obtain the execution result, so the realization of the result is complicated
In order to reduce the difficulty of parallel programming, Java5 introduced Future, which is used to build complex parallel operations. When internally returning data, it does not directly return results. For example, the Future object is returned in the picture. In this way, the waiting time of synchronous execution can be used to execute other services, and the results can be obtained when needed. Java official description:
Future represents the result of asynchronous computation. Methods are provided to check that a calculation is complete, wait for it to complete, and retrieve the result of the calculation. The get method can be used to retrieve the results only after the calculation is complete, blocking it if necessary until it is ready to thread. Cancel execution through the cancel method. Additional methods are provided to determine whether a task is completed properly or cancelled. Once the calculation is complete, it cannot be cancelled.
The Future interface
In the Future interface, five interface methods are provided
- boolean cancel(boolean mayInterruptIfRunning);
If the task is canceled, true is returned. The mayInterruptIfRunning input parameter indicates whether a task in progress is allowed to be canceled
- boolean isCancelled();
Cancel or not: Returns a Boolean value indicating whether the task was cancelled successfully
- boolean isDone();
Completed or not: Returns a Boolean value indicating whether the execution is completed
- V get() throws InterruptedException, ExecutionException;
Get execution result: Return the Future object, get the execution result, if the task is not completed will block until the task is completed
- V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
Gets the execution result and sets the timeout, or throws a TimeoutException if it times out
The Future application
Future applications are usually executed in conjunction with ExecutorService, which has a thread pool to start a thread to perform Future tasks
ExecutorService executor = Executors.newCachedThreadPool();
futureA = executor.submit((Callable) () -> getLikeCount());
futureB = executor.submit((Callable) () -> getWatchCount());
futureC = executor.submit((Callable) () -> getForwardCount());
report(futureA, futureB, futureC);
Copy the code
Execution Result:
main
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2Main likes:100Reading:200Forwarding number:300
Copy the code
As can be seen from the execution results, the three Future tasks are executed separately by three threads, and then the method of generating the report is completed in the main thread
In the process of execution, both are likely to be parallel to serial, waiting for the results at the end, due to some reason one of the tasks, there has been no return a result, the future, the get () will have been waiting for the outcome of the task, zha into subsequent logic has been unable to perform, has caused the serialization, a direct result of did not reach the purpose of serial. For this situation, we can take another method of GET, pass in the waiting time, if the execution times out, throw an exception, handling the exception does not block the subsequent execution process. For Future, it can support concurrent execution of tasks and obtain task results in the order of submission. It is recommended to obtain task results through high-speed CPU polling, but this method consumes resources. Not recommended.
FutureTask class
To combine the advantages of both Fucture and Runable, an interface, RunnableFuture, is officially provided in the java.util.Concurrent package
public interface RunnableFuture<V> extends Runnable.Future<V> {
void run(a);
}
Copy the code
The implementation of the RunnableFuture interface is officially provided with the FutureTask class implementation, so that tasks can be handed to executors like futures or executed directly by Run in Runnable. Its original intention is to make up for the lack of Thread.
FutureTask use:
// Create a thread pool
ExecutorService executorService = Executors.newFixedThreadPool(5);
List<FutureTask<Integer>> taskList = new ArrayList<>();
taskList.add(new FutureTask<>(() -> getLikeCount()));
taskList.add(new FutureTask<>(() -> getWatchCount()));
taskList.add(new FutureTask<>(() -> getForwardCount()));
// Submit the task to the thread pool for execution
taskList.forEach(item -> executorService.submit(item));
// iterate to get the result
for (FutureTask<Integer> futureTask : taskList) {
//FutureTask's get method blocks automatically until the result is retrieved
futureTask.get();
}
Copy the code
FutureTask is rarely used in practice because the order in which the results are fetched is uncertain. When an Executor submits multiple tasks and gets the results of their execution, the task needs to be iterated and then the get method is called to get the results of the task, but this has the same old problem: If a task takes a long time to execute, the thread blocks until the task is completed, and the parallelism becomes serial.
In order to solve the problem of Future and FutureTask, Java5 also proposes: CompletableFuture is a better solution, CompletionService specific use and principle process, please pay attention to the blog’s other articles, but it is used by everyone well.