Callable
Why Callable when you have Runnable?
Let’s take a look at the Callable interface:
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
In the case of Runnable, it cannot return a value. There are other ways to save the results of a thread’s execution, such as writing a log file to the Runnable method or modifying a shared object. Belong to curve save country, efficiency is really not high.
In fact, in many cases, when executing a thread, we expect to get the result of the task being executed, that is, we need to get the return value, such as requesting the network, querying the database, etc. The V in the interface represents the return value.
The second thing is that you can’t throw an Exception. If we look at the Callable interface definition, we throw an Exception, but we don’t throw a Runnable. Runnable can only be written like this:
Runnable runnable = new Runnable() {
/** * Cannot declare an Exception on the run method and cannot throw a checked Exception within the run method unless a try catch is used */
@Override
public void run(a) {
try {
throw new IOException();
} catch(IOException e) { e.printStackTrace(); }}}Copy the code
Finally, compare Runnable and Callable
- Method name, Callable executes call() and Runnable executes run();
- The Callable task returns a value, while the Runnable task does not return a value.
- To throw an exception, the call() method can throw an exception, but the run() method cannot.
- Callable has a Future class that can be used to check the execution status of a task, cancel the execution of a task, and obtain the results of the task execution. Runnable cannot do all these functions. Callable is more powerful than Runnable.
Future
Callable can return a value from a Callable. Get it from the Future class’s get method.
Thus, the Future acts as a store that stores the task results of the Callable’s Call method. In addition, we can also determine whether a task has been completed by using the isDone method of the Future, cancel the task by using the cancel method, or obtain the result of the task within a limited time, etc. In short, the Future has rich functions.
How to Create a Future
One is through thread pools, which we talked about earlier in thread pools, thread pools in Essence.
ExecutorService service = Executors.newFixedThreadPool(10);
Future<Integer> future = service.submit(new CallableTask());
// block to get the result
Integer rs = future.get();
Copy the code
Another is created through FutureTask
FutureTask<Integer> integerFutureTask = new FutureTask<>(new CallableTask());
// Start the thread
new Thread(integerFutureTask).start();
// block to get the result
Integer rs=integerFutureTask.get();
Copy the code
With that in mind, let’s take a look at the methods in Future:
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
The get () method
-
The most common one is that when you perform GET, the task has been completed and you can return immediately to obtain the result of the task execution.
-
The task has no result. If the task has not started or is in progress, when we call GET, we block the current thread and return the result until the task is complete.
-
We throw an exception during the execution of the task, and once we do that, when we call get, we throw an ExecutionException, regardless of the type of exception we throw when we execute the call method, The exceptions you get when you execute the GET method are executionExceptions.
-
The task was cancelled, and if the task is cancelled, a CancellationException will be thrown when we use the get method to get the result.
-
If the call method completes the task within the specified time, then get will return normally. If the call method completes the task within the specified time, then get will return normally. However, if the task has not been completed by the specified time, the GET method throws a TimeoutException indicating that a timeout has occurred.
IsDone () method
This method is used to determine whether the current task is completed. Note that this method is completed if it returns true and not yet completed if it returns false.
However, a return of true does not mean that the task was successfully executed, for example, an exception was thrown in the middle of the task. So in this case, for this isDone method, it’s actually going to return true, because for it, even though there was an exception, the task is not going to be executed in the future, it’s actually done. So when the isDone method returns true, it does not mean that the task was successfully executed, only that it completed.
The cancel () method
-
The cancel method returns true if the task has not yet been executed and the task is cancelled normally once it is called and will not be executed in the future.
-
If the task has completed or has been cancelled before, cancel fails and returns false. Because tasks, whether completed or cancelled, can’t be cancelled again.
-
While the task is executing, executing the cancel method will not cancel the task directly, but will make a judgment based on the parameters we pass in. The cancel method must pass in a parameter called mayInterruptIfRunning. What does it mean? If true is passed in, the thread executing the task will receive an interrupt signal, and the executing task will probably have some logic to handle the interrupt and stop, which makes sense. If false is passed in, the running task is not interrupted, that is, the cancel has no effect and the Cancel method returns false.
IsCancelled () method
Determine whether or not to cancel, which is used in conjunction with the cancel method, is relatively simple.
Look at the FutureTask class diagram below:
Since futureTask can be thrown into a Thread class, it must inherit the Runnable interface and implement the Run method. Since you can call the get() method, you must inherit the Future interface, as shown in the class diagram above.
If we look at the source code, if we look at the run() method, it’s very simple, and the logic that it does is the call method in the Callable, and it ends up saving the calculated result to the outcome, and then waking up the blocked thread.
If you look at the get() method, it’s very simple. If the task is done, the outcome is returned, otherwise it’s put in a blocking queue, similar to AQS.Introduction to ReentrantLock and AQS Finally, look at the flow chart:
CompletableFuture
Here’s how Future/Callable works, and here’s CompletableFuture.
CompletableFuture improves the Future, mainly in the get() method. Instead of waiting for the main thread to continue operations depending on the result of the task execution, it can directly pass in a callback object. When the asynchronous task is completed, the callback object is automatically called. The asynchronous callback notification function is implemented.
In addition, CompletableFuture provides very powerful functions, such as executing callback objects in non-task threads or in task threads. It provides functional programming capability and simplifies the complexity of asynchronous programming. Provides the combination and transformation functions of multiple CompletableFutures.
If you look at the class diagram, the CompletionStage and Future interfaces are implemented.Future is the Future mentioned above, and there are 5 methods in it. CompletionStage represents a stage of task execution. Each asynchronous task will return a new CompletionStage object. We can serial, parallel or aggregate multiple CompletionStage objects to carry out subsequent operations in the next stage.This is to implement the automatic callback function after the execution of asynchronous tasks.
The construction of CompletableFuture
CompletableFuture provides four static methods to build an asynchronous event, as follows.
- SupplyAsync (Supplier Supplier) : Asynchronous execution method with a return value, passing in a functional interface that returns a new CompletableFuture object. By default, ForkJoinPool.commonPool() is used as a thread pool to execute asynchronous tasks.
- SupplyAsync (Supplier Supplier,Executor Executor) : Asynchronous execution method with a return value, with an additional Executor parameter indicating that a custom thread pool is used to perform a task.
- RunAsync (Runnable Runnable) : Asynchronous execution method with no return value that passes in a Runnable and returns a new CompletableFuture object. By default, ForkjoinPool.commonPool() is used as a thread pool to execute asynchronous tasks.
- RunAsync (Runnable Runnable,Executor Executor) : Asynchronous execution method with no return value, with an additional Executor parameter indicating that a custom thread pool is used to execute a task.
Here’s a simple use of CompletableFuture:
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture cf1 = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName() + ": Execute a task asynchronously");
});
// Get the execution result by blocking
System.out.println(cf1.get());
CompletableFuture cf2 = CompletableFuture.supplyAsync(() -> "Hello World").thenAccept(result -> {
System.out.println(result);
});
// Move on to something else
/ /...
}
Copy the code
Cf1 executes a task using the default ForkJoinPool thread pool with no return value. Cf1.get () blocks the null value because there is no return value.
Cf2 executes a task with a return value, which does one thing: return Hello World, at which point the main thread can continue to execute and do other things. After the task completes, thenAccept receives the returned Hello World and prints it out.
CompletableFuture method introduction
So if we look at the CompletableFuture class there are 38 methods, a lot of them, and I’m going to sort them out for you.
Method of obtaining results
The CompletableFuture class implements the Future interface, so it can start to actively block or poll results like a Future.
- Get () to obtain the execution result of an asynchronous task based on blocking.
- Get (Long timeout, TimeUnit Unit) : obtains the asynchronous execution result by blocking with a timeout period.
- Join () does the same thing as get(), except that get() allows interrupts by throwing InterruptedException, whereas join() does not.
- GetNow (T valueIfAbsent), this method is a bit special. If the current task has already been executed, the result is returned. Otherwise, valueIfAbsent is returned.
Another interesting method in the CompletableFuture class is complete(T Value), which completes the calculation by setting value to the return value of the CompletableFuture and waking up the thread that blocked in the above method.
In the example below, we create two threads, T1 and T2, which are blocked by the completableFuture.get() method. When we call cf.complete(“Finish”), we assign a value to it, and get() gets its value before we can proceed.
public class CompleteExample {
static class ClientThread implements Runnable {
private CompletableFuture completableFuture;
public ClientThread(CompletableFuture completableFuture) {
this.completableFuture = completableFuture;
}
@Override
public void run(a) {
try {
System.out.println(Thread.currentThread().getName() + ":" +
completableFuture.get()); / / blocking
} catch (InterruptedException e) {
e.printStackTrace();
} catch(ExecutionException e) { e.printStackTrace(); }}}public static void main(String[] args) {
CompletableFuture cf = new CompletableFuture();
new Thread(new ClientThread(cf), "t1").start();
new Thread(new ClientThread(cf), "t2").start();
// Execute some logic
cf.complete("Finish");
//exception
//cf.completeExceptionally(e);}}Copy the code
Pure consumption type approach
Pure consumption-type methods, which rely on the result of the previous asynchronous task as the parameter of the current function to perform the next calculation, are characterized by no new calculation value, such methods include the Accept keyword. Methods containing nine Accept keywords in the CompletionStage can be divided into three categories: Depends on the completion of a single CompletionStage task, depends on the completion of both completionstages task, depends on the completion of either of two completionstages task.
// The current thread executes synchronously
public CompletionStage<Void> thenAccept(Consumer<? super T> action);
// Use the ForkJoinPool.commonPool to execute an action
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
// Use the custom thread pool to execute the action
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T>
action,Executor executor);
public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U>
other,BiConsumer<? super T, ? super U> action);
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<?
extends U> other,BiConsumer<? super T, ? super U> action);
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<?
extends U> other,BiConsumer<? super T, ? super U> action,Executor executor);
public CompletionStage<Void> acceptEither(CompletionStage<? extends T>
other,Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T>
other,Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T>
other,Consumer<? super T> action,Executor executor);
Copy the code
The thenAcceptBoth() method is used when task1 and task2 both return values and then print them together.
public class AcceptExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "AcceptBot");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Message");
task1.thenAcceptBoth(task2, (r1, r2) -> {
System.out.println("result: "+ r1 + r2); }); }}Copy the code
A method with a return value type
A method with a return value type does the next calculation with the result of the previous asynchronous task and produces a new CompletionStage object with a return value.
In the CompletionStage, nine methods that return results are defined, which can also be divided into three types: Depends on the completion of a single CompletionStage task, depends on the completion of both completionstages task, depends on the completion of either of two completionstages task.
public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U>
fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U>
fn,Executor executor);
public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U>
other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends
U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends
U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);
public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T>
other,Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends
T> other,Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends
T> other,Function<? super T, U> fn,Executor executor);
Copy the code
ThenApply () method
I’m going to create a new task return Hello, thenApply is going to get the value and concatenate it with world, and then return the value, and then get the value.
public class ApplyExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture cf = CompletableFuture.supplyAsync(() -> "hello ").thenApply(r -> {
return r + "world"; }); System.out.println(cf.get()); }}Copy the code
ThenCombineAsync () method
ThenCombineAsync is used to bring the values of task1 and Task2 back to the future value.
public class CombineDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture cf = CompletableFuture.supplyAsync(() -> "Combine")
.thenCombineAsync(CompletableFuture.supplyAsync(() -> "Message"), (r1, r2) -> r1 + r2); System.out.println(cf.get()); }}Copy the code
A method that neither consumes nor returns
There are also 9 ways
public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor
executor);
public CompletionStage<Void> runAfterBoth(CompletionStage
other,Runnable action);
public CompletionStage<Void> runAfterBothAsync(CompletionStage
other,Runnable action);
public CompletionStage<Void> runAfterBothAsync(CompletionStage
other,Runnable action,Executor executor);
public CompletionStage<Void> runAfterEither(CompletionStage
other,Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage
other,Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage
other,Runnable action,Executor executor);
Copy the code
I’m going to create two new tasks, one is return Both, and the other is return Message, and when they’re done, because run doesn’t consume or return, so the input parameter is 0, you don’t need your arguments, and you don’t return, so there’s no return.
public class RunExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture cxf = CompletableFuture.supplyAsync(() -> "Both")
.runAfterBoth(CompletableFuture.supplyAsync(() -> "Message"), () -> {
System.out.println("Done"); }); System.out.println(cxf.get()); }}Copy the code
Multi-task combination
public <U> CompletionStage<U> thenCompose(Function<? super T, ? extends
CompletionStage<U>> fn);
public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends
CompletionStage<U>> fn);
public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends
CompletionStage<U>> fn,Executor executor);
Copy the code
Exception handling
There are three ways to handle exceptions
- WhenComplete: Represents the method that is triggered when the task is completed. It is characterized by the ability to trigger specific action methods regardless of whether the CompletionStage task in front finishes normally or fails.
- Handle: Indicates that after the completion of the pre-task, the FN function of handle will be executed no matter whether the pre-task execution status is normal or abnormal. Its function is almost the same as whenComplete, except that Handle is a method with return value type.
- Exceptionally: accepts a FN function and passes it as a parameter when an exception occurs on the previous CompletionStage.
This is written in an example, and the specific type is selected by the partner according to the specific scene.Finally, CompletableFuture has a lot of methods, several of which are introduced in this paper. Most of them are used by small partners slowly in the actual development process, practice makes perfect, some methods lack application scenarios and it is difficult to give examples, and the parameters passed in these methods are functional interfaces. Lambda expressions java8 new features, this is also need to learn, otherwise will not understand.Thanks for watching.