Java中的Runnable,Callable,Future,FutureTask的比較
Java中存在Runnable、Callable、Future、FutureTask這幾個與線程相關的類或者接口,在Java中也是比較重要的幾個概念,我們通過下面的簡單示例來了解一下它們的作用于區別。
Runnable
其中Runnable應該是我們最熟悉的接口,它只有一個run()函數,用于將耗時操作寫在其中, 該函數沒有返回值 。然后使用某個線程去執行該runnable即可實現多線程,Thread類在調用start()函數后就是執行的是Runnable的run()函數。Runnable的聲明如下 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); } |
Callable
Callable與Runnable的功能大致相似,Callable中有一個call()函數,但是 call()函數有返回值 ,而Runnable的run()函數不能將結果返回給客戶程序。Callable的聲明如下 :
1
2
3
4
5
6
7
8
9
10
|
@FunctionalInterface 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() throws Exception; } |
可以看到,這是一個泛型接口,call()函數返回的類型就是客戶程序傳遞進來的V類型。
Future
Executor就是Runnable和Callable的調度容器,Future就是對于具體的Runnable或者Callable任務的執行結果進行取消、查詢是否完成、獲取結果、設置結果操作。get方法會阻塞,直到任務返回結果(Future簡介)。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
|
* @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; } |
FutureTask
FutureTask則是一個RunnableFuture< V>,而RunnableFuture實現了Runnbale又實現了Futrue< V>這兩個接口:
1
2
3
|
public class FutureTask<V> implements RunnableFuture<V> { ...... } |
RunnableFuture
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/** * A {@link Future} that is {@link Runnable}. Successful execution of * the {@code run} method causes completion of the {@code Future} * and allows access to its results. * @see FutureTask * @see Executor * @since 1.6 * @author Doug Lea * @param <V> The result type returned by this Future's {@code get} method */ public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run(); } |
另外FutureTask還可以包裝Runnable和Callable< V>, 由構造函數注入依賴。
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
|
/** * Creates a {@code FutureTask} that will, upon running, execute the * given {@code Callable}. * * @param callable the callable task * @throws NullPointerException if the callable is null */ public FutureTask(Callable<V> callable) { if (callable == null ) throw new NullPointerException(); this .callable = callable; this .state = NEW; // ensure visibility of callable } /** * Creates a {@code FutureTask} that will, upon running, execute the * given {@code Runnable}, and arrange that {@code get} will return the * given result on successful completion. * * @param runnable the runnable task * @param result the result to return on successful completion. If * you don't need a particular result, consider using * constructions of the form: * {@code Future<?> f = new FutureTask<Void>(runnable, null)} * @throws NullPointerException if the runnable is null */ public FutureTask(Runnable runnable, V result) { this .callable = Executors.callable(runnable, result); this .state = NEW; // ensure visibility of callable } |
可以看到,Runnable注入會被Executors.callable()函數轉換為Callable類型,即FutureTask最終都是執行Callable類型的任務。該適配函數的實現如下 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/** * Returns a {@link Callable} object that, when * called, runs the given task and returns the given result. This * can be useful when applying methods requiring a * {@code Callable} to an otherwise resultless action. * @param task the task to run * @param result the result to return * @param <T> the type of the result * @return a callable object * @throws NullPointerException if task null */ public static <T> Callable<T> callable(Runnable task, T result) { if (task == null ) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); } |
RunnableAdapter適配器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/** * A callable that runs given task and returns given result */ static final class RunnableAdapter<T> implements Callable<T> { final Runnable task; final T result; RunnableAdapter(Runnable task, T result) { this .task = task; this .result = result; } public T call() { task.run(); return result; } } |
由于FutureTask實現了Runnable,因此它既可以通過Thread包裝來直接執行,也可以提交給ExecuteService來執行。并且還可以直接通過get()函數獲取執行結果,該函數會阻塞,直到結果返回。
因此FutureTask既是Future、Runnable,又是包裝了Callable(如果是Runnable最終也會被轉換為Callable ), 它是這兩者的合體。
完整示例:
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
package com.stay4it.rx; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; public class FutureTest { public static class Task implements Runnable { @Override public void run() { // TODO Auto-generated method stub System.out.println( "run" ); } } public static class Task2 implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println( "call" ); return fibc( 30 ); } } /** * runnable, 無返回值 */ public static void testRunnable(){ ExecutorService executorService = Executors.newCachedThreadPool(); Future<String> future = (Future<String>) executorService.submit( new Task()); try { System.out.println(future.get()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } executorService.shutdown(); } /** * Callable, 有返回值 */ public static void testCallable(){ ExecutorService executorService = Executors.newCachedThreadPool(); Future<Integer> future = (Future<Integer>) executorService.submit( new Task2()); try { System.out.println(future.get()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } executorService.shutdown(); } /** * FutureTask則是一個RunnableFuture<V>,即實現了Runnbale又實現了Futrue<V>這兩個接口, * 另外它還可以包裝Runnable(實際上會轉換為Callable)和Callable * <V>,所以一般來講是一個符合體了,它可以通過Thread包裝來直接執行,也可以提交給ExecuteService來執行 * ,并且還可以通過v get()返回執行結果,在線程體沒有執行完成的時候,主線程一直阻塞等待,執行完則直接返回結果。 */ public static void testFutureTask(){ ExecutorService executorService = Executors.newCachedThreadPool(); FutureTask<Integer> futureTask = new FutureTask<Integer>( new Task2()); executorService.submit(futureTask); try { System.out.println(futureTask.get()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } executorService.shutdown(); } /** * FutureTask則是一個RunnableFuture<V>,即實現了Runnbale又實現了Futrue<V>這兩個接口, * 另外它還可以包裝Runnable(實際上會轉換為Callable)和Callable * <V>,所以一般來講是一個符合體了,它可以通過Thread包裝來直接執行,也可以提交給ExecuteService來執行 * ,并且還可以通過v get()返回執行結果,在線程體沒有執行完成的時候,主線程一直阻塞等待,執行完則直接返回結果。 */ public static void testFutureTask2(){ ExecutorService executorService = Executors.newCachedThreadPool(); FutureTask<Integer> futureTask = new FutureTask<Integer>( new Runnable() { @Override public void run() { // TODO Auto-generated method stub System.out.println( "testFutureTask2 run" ); } },fibc( 30 )); executorService.submit(futureTask); try { System.out.println(futureTask.get()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } executorService.shutdown(); } public static void main(String[] args) { testCallable(); } /** * 效率低下的斐波那契數列, 耗時的操作 * * @param num * @return */ static int fibc( int num) { if (num == 0 ) { return 0 ; } if (num == 1 ) { return 1 ; } return fibc(num - 1 ) + fibc(num - 2 ); } } |
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!