Like Share Discussion Bookmark Smile

J.J. Huang   2019-04-24   Spring Boot   瀏覽次數:

SpringBoot - 第三十六章 | @Async 實現異步調用:使用Future以及定義超時

在前面已經閱讀研究了幾篇關於使用@Async實現異步調用的內容,雖然多數都是參考別人的文章,但是自己動手做實踐才是最重要的,這邊又參考了一篇使用Future以及定義超時。

關於@Async註解,可以看看之前的文章:
SpringBoot - 第三十三章 | @Async 實現異步調用
SpringBoot - 第三十四章 | @Async 實現異步調用:自定義線程池
SpringBoot - 第三十五章 | @Async 實現異步調用:ThreadPoolTask​​Scheduler線程池的優雅關閉

這邊繼續沿用 chapter33 的專案來做範例。


什麼是Future?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

package java.util.concurrent;

/**
* A {@code Future} represents the result of an asynchronous
* computation. Methods are provided to check if the computation is
* complete, to wait for its completion, and to retrieve the result of
* the computation. The result can only be retrieved using method
* {@code get} when the computation has completed, blocking if
* necessary until it is ready. Cancellation is performed by the
* {@code cancel} method. Additional methods are provided to
* determine if the task completed normally or was cancelled. Once a
* computation has completed, the computation cannot be cancelled.
* If you would like to use a {@code Future} for the sake
* of cancellability but not provide a usable result, you can
* declare types of the form {@code Future<?>} and
* return {@code null} as a result of the underlying task.
*
* <p>
* <b>Sample Usage</b> (Note that the following classes are all
* made-up.)
* <pre> {@code
* interface ArchiveSearcher { String search(String target); }
* class App {
* ExecutorService executor = ...
* ArchiveSearcher searcher = ...
* void showSearch(final String target)
* throws InterruptedException {
* Future<String> future
* = executor.submit(new Callable<String>() {
* public String call() {
* return searcher.search(target);
* }});
* displayOtherThings(); // do other things while searching
* try {
* displayText(future.get()); // use future
* } catch (ExecutionException ex) { cleanup(); return; }
* }
* }}</pre>
*
* The {@link FutureTask} class is an implementation of {@code Future} that
* implements {@code Runnable}, and so may be executed by an {@code Executor}.
* For example, the above construction with {@code submit} could be replaced by:
* <pre> {@code
* FutureTask<String> future =
* new FutureTask<String>(new Callable<String>() {
* public String call() {
* return searcher.search(target);
* }});
* executor.execute(future);}</pre>
*
* <p>Memory consistency effects: Actions taken by the asynchronous computation
* <a href="package-summary.html#MemoryVisibility"> <i>happen-before</i></a>
* actions following the corresponding {@code Future.get()} in another thread.
*
* @see FutureTask
* @see Executor
* @since 1.5
* @author Doug Lea
* @param <V> The result type returned by this Future's {@code get} method
*/
public interface Future<V> {

/**
* Attempts to cancel execution of this task. This attempt will
* fail if the task has already completed, has already been cancelled,
* or could not be cancelled for some other reason. If successful,
* and this task has not started when {@code cancel} is called,
* this task should never run. If the task has already started,
* then the {@code mayInterruptIfRunning} parameter determines
* whether the thread executing this task should be interrupted in
* an attempt to stop the task.
*
* <p>After this method returns, subsequent calls to {@link #isDone} will
* always return {@code true}. Subsequent calls to {@link #isCancelled}
* will always return {@code true} if this method returned {@code true}.
*
* @param mayInterruptIfRunning {@code true} if the thread executing this
* task should be interrupted; otherwise, in-progress tasks are allowed
* to complete
* @return {@code false} if the task could not be cancelled,
* typically because it has already completed normally;
* {@code true} otherwise
*/
boolean cancel(boolean mayInterruptIfRunning);

/**
* Returns {@code true} if this task was cancelled before it completed
* normally.
*
* @return {@code true} if this task was cancelled before it completed
*/
boolean isCancelled();

/**
* Returns {@code true} if this task completed.
*
* Completion may be due to normal termination, an exception, or
* cancellation -- in all of these cases, this method will return
* {@code true}.
*
* @return {@code true} if this task completed
*/
boolean isDone();

/**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
*/
V get() throws InterruptedException, ExecutionException;

/**
* Waits if necessary for at most the given time for the computation
* to complete, and then retrieves its result, if available.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
* @throws TimeoutException if the wait timed out
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}

在上面的註釋中我們能知道Future用來代表異步的結果,對於具體的Runnable或者Callable任務的執行結果進行取消、查詢是否完成、獲取結果的接口。必要時可以通過get方法獲取執行結果,該方法會阻塞直到任務返回結果。簡而言之就是提供一個異步運算結果的一個建模。它可以讓我們把耗時的操作從我們本身的調用線程中釋放出來,只需要完成後再進行回調。就好像我們去飯店裡面吃飯,不需要你去煮飯,而你這個時候可以做任何事,然後飯煮好後就會回調你去吃。

  • 共聲明五個方法:
方法 說明
cancel 方法用來取消任務,如果取消任務成功則返回true,如果取消任務失敗則返回false。參數mayInterruptIfRunning表示是否允許取消正在執行卻沒有執行完畢的任務,如果設置true,則表示可以取消正在執行過程中的任務。如果任務已經完成,則無論mayInterruptIfRunning為true還是false,此方法肯定返回false,即如果取消已經完成的任務會返回false;如果任務正在執行,若mayInterruptIfRunning設置為true,則返回true,若mayInterruptIfRunning設置為false ,則返回false;如果任務還沒有執行,則無論mayInterruptIfRunning為true還是false,肯定返回true。
isCancelled 方法表示任務是否被取消成功,如果在任務正常完成前被取消成功,則返回 true。
isDone 方法表示任務是否已經完成,若任務完成,則返回true;
get() 方法用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回;
get(long timeout, TimeUnit unit) 用來獲取執行結果,如果在指定時間內,還沒獲取到結果,就直接返回null。

總結Future提供了三種功能,判斷任務是否完成、能夠中斷任務、能夠獲取任務執行結果。


什麼是CompletableFuture?

CompletableFuture是JDK8提出的一個支持非阻塞的多功能的Future,同樣也是實現了Future接口。

修改 CompleteAsyncTask

建立 測試案例

測試結果

執行在五秒內

執行超過五秒

我們在get方法中還定義了該線程執行的超時時間,通過執行這個測試我們可以觀察到執行時間超過5秒的時候,這裡會拋出超時異常,該執行線程就能夠因執行超時而釋放迴線程池,不至於一直阻塞而佔用資源。

註:以上參考了
异步技巧之CompletableFuture
程序猿DDSpring Boot使用@Async实现异步调用:使用Future以及定义超时 文章。