執行緒執行 之 Runnable Callable Future ,FutureTask ExcutorService概覽
Runnable 和Callable介面的區別
執行緒的建立方式中有兩種,一種是實現Runnable介面,另一種是繼承Thread,但是這兩種方式都有個缺點,那就是在任務執行完成之後無法獲取返回結果,於是就有了Callable介面,Future介面與FutureTask類的配合取得返回的結果。
我們先回顧一下java.lang.Runnable介面,就宣告瞭run(),其返回值為void,當然就無法獲取結果。
public interface Runnable { public abstract void run();
}
而Callable的介面定義如下public interface Callable<V> { V call() throws Exception;
}
該介面宣告瞭一個名稱為call()的方法,同時這個方法可以有返回值V,也可以丟擲異常。嗯,對該介面我們先了解這麼多就行,下面我們來說明如何使用,
ExecutorService 介面的用途:(執行緒池的高階抽象介面)
無論是Runnable介面的實現類還是Callable介面的實現類,都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor執行,ThreadPoolExecutor或ScheduledThreadPoolExecutor都實現了ExcutorService介面,而因此Callable需要和Executor框架中的ExcutorService結合使用,我們先看看ExecutorService提供的方法:<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
第一個方法:submit提交一個實現Callable介面的任務,並且返回封裝了非同步計算結果的Future。
第二個方法:submit提交一個實現Runnable介面的任務,並且指定了在呼叫Future的get方法時返回的result物件。(不常用)
第三個方法:submit提交一個實現Runnable介面的任務,並且返回封裝了非同步計算結果的Future。
因此我們只要建立好我們的執行緒物件(實現Callable介面或者Runnable介面),然後通過上面3個方法提交給執行緒池去執行即可。
這三個方法的內部實現:
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
其中newTaskFor 的實現
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
可以看出不管submit 的是什麼內容,都會被封裝成 FutureTask。
execue(ftask) 的邏輯請參考 https://blog.csdn.net/xiaoliuliu2050/article/details/87882438
還有點要注意的是,除了我們自己實現Callable物件外,我們還可以使用工廠類Executors來把一個Runnable物件包裝成Callable物件。Executors工廠類提供的方法如下:
public static Callable<Object> callable(Runnable task)
public static <T> Callable<T> callable(Runnable task, T result)
Future<V>介面
Future<V>介面是用來獲取非同步計算結果的,說白了就是對具體的Runnable或者Callable物件任務執行的結果進行獲取(get()),取消(cancel()),判斷是否完成等操作。我們看看Future介面的原始碼:
-
public interface Future<V> {
-
boolean cancel(boolean mayInterruptIfRunning);
-
boolean isCancelled();
-
boolean isDone();
-
V get() throws InterruptedException, ExecutionException;
-
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
-
}
方法解析:
V get() :獲取非同步執行的結果,如果沒有結果可用,此方法會阻塞直到非同步計算完成。
V get(Long timeout , TimeUnit unit) :獲取非同步執行結果,如果沒有結果可用,此方法會阻塞,但是會有時間限制,如果阻塞時間超過設定的timeout時間,該方法將丟擲異常。
boolean isDone() :如果任務執行結束,無論是正常結束或是中途取消還是發生異常,都返回true。
boolean isCanceller() :如果任務完成前被取消,則返回true。
boolean cancel(boolean mayInterruptRunning) :如果任務還沒開始,執行cancel(...)方法將返回false;如果任務已經啟動,執行cancel(true)方法將以中斷執行此任務執行緒的方式來試圖停止任務,如果停止成功,返回true;當任務已經啟動,執行cancel(false)方法將不會對正在執行的任務執行緒產生影響(讓執行緒正常執行到完成),此時返回false;當任務已經完成,執行cancel(...)方法將返回false。mayInterruptRunning參數列示是否中斷執行中的執行緒。
通過方法分析我們也知道實際上Future提供了3種功能:(1)能夠中斷執行中的任務(2)判斷任務是否執行完成(3)獲取任務執行完成後額結果。
但是我們必須明白Future只是一個介面,我們無法直接建立物件,因此就需要其實現類FutureTask登場啦。
FutureTask類
我們先來看看FutureTask的實現,FutureTask類實現了RunnableFuture介面,
public class FutureTask<V> implements RunnableFuture<V> ;
我們看一下RunnableFuture介面的實現:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
分析:FutureTask除了實現了Future介面外還實現了Runnable介面(即可以通過Runnable介面實現執行緒,也可以通過Future取得執行緒執行完後的結果),因此FutureTask也可以直接提交給Executor執行。
最後我們給出FutureTask的兩種建構函式:
public FutureTask(Callable<V> callable) {}
public FutureTask(Runnable runnable, V result) {}
Callable<V>/Future<V>/FutureTask的具體使用場景(封裝了非同步獲取結果的Future!!!)
假如有這樣的場景,我們現在需要計算一個資料,而這個資料的計算比較耗時,而我們後面的程式也要用到這個資料結果,那麼這個時Callable豈不是最好的選擇?我們可以開設一個執行緒去執行計算,而主執行緒繼續做其他事,而後面需要使用到這個資料時,我們再使用Future獲取不就可以了嗎?下面我們就來編寫一個這樣的例項
方式1 使用Callable+Future獲取執行結果
Callable實現類如下:
public class CallableDemo implements Callable<Integer> {
-
private int sum;
-
@Override
-
public Integer call() throws Exception {
-
System.out.println("Callable子執行緒開始計算啦!");
-
Thread.sleep(2000);
-
for(int i=0 ;i<5000;i++){
-
sum=sum+i;
-
}
-
System.out.println("Callable子執行緒計算結束!");
-
return sum;
-
}
-
}
Callable執行測試類如下:
-
public class CallableTest {
-
public static void main(String[] args) {
-
//建立執行緒池
-
ExecutorService es = Executors.newSingleThreadExecutor();
-
//建立Callable物件任務
-
CallableDemo calTask=new CallableDemo();
-
//提交任務並獲取執行結果
-
Future<Integer> future =es.submit(calTask);
-
//關閉執行緒池
-
es.shutdown();
-
try {
-
Thread.sleep(2000);
-
System.out.println("主執行緒在執行其他任務");
-
if(future.get()!=null){
-
//輸出獲取到的結果
-
System.out.println("future.get()-->"+future.get());
-
}else{
-
//輸出獲取到的結果
-
System.out.println("future.get()未獲取到結果");
-
}
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
System.out.println("主執行緒在執行完成");
-
}
-
}
執行結果:
-
Callable子執行緒開始計算啦!
-
主執行緒在執行其他任務
-
Callable子執行緒計算結束!
-
future.get()-->12497500
-
主執行緒在執行完成
方式 2 使用Callable+FutureTask獲取執行結果
-
public class CallableTest {
-
public static void main(String[] args) {
-
//建立執行緒池
-
ExecutorService es = Executors.newSingleThreadExecutor();
-
//建立Callable物件任務
-
CallableDemo calTask=new CallableDemo();
-
//建立FutureTask
-
FutureTask<Integer> futureTask=new FutureTask<>(calTask);
-
//執行任務
-
es.submit(futureTask);
-
//關閉執行緒池
-
es.shutdown();
-
try {
-
Thread.sleep(2000);
-
System.out.println("主執行緒在執行其他任務");
-
if(futureTask.get()!=null){
-
//輸出獲取到的結果
-
System.out.println("futureTask.get()-->"+futureTask.get());
-
}else{
-
//輸出獲取到的結果
-
System.out.println("futureTask.get()未獲取到結果");
-
}
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
System.out.println("主執行緒在執行完成");
-
}
-
}
執行結果:
-
Callable子執行緒開始計算啦!
-
主執行緒在執行其他任務
-
Callable子執行緒計算結束!
-
futureTask.get()-->12497500
-
主執行緒在執行完成
相關文章
- Java多執行緒之Callable,Future,FutureTaskJava執行緒
- Java多執行緒——Callable、Future和FutureTaskJava執行緒
- Java多執行緒21:多執行緒下的其他元件之CyclicBarrier、Callable、Future和FutureTaskJava執行緒元件
- 大話Android多執行緒(四) Callable、Future和FutureTaskAndroid執行緒
- 搞懂Runnable Callable Future FutureTask 及應用
- Java多執行緒-Callable和FutureJava執行緒
- Runnable、Callable、Executor、Future、FutureTask關係解讀
- Java多執行緒之FutureTaskJava執行緒
- CLR 執行緒概覽執行緒
- 溫故知新----執行緒之Runnable與Callable介面的本質區別執行緒
- java多執行緒程式設計,Thread,Runnable,Future相關知識Java執行緒程式設計thread
- java多執行緒系列之future機制Java執行緒
- java 多執行緒設計模式之futureJava執行緒設計模式
- Runnable介面實現多執行緒執行緒
- Runnable,Callable,Future關係淺析
- 多執行緒-執行緒控制之休眠執行緒執行緒
- 多執行緒-執行緒控制之加入執行緒執行緒
- 多執行緒-執行緒控制之禮讓執行緒執行緒
- 執行緒池續:你必須要知道的執行緒池submit()實現原理之FutureTask!執行緒MIT
- Future --- 多執行緒設計模式執行緒設計模式
- new Handler().postDelayed(new Runnable())是否執行在主執行緒?執行緒
- 多執行緒-執行緒控制之守護執行緒執行緒
- 執行緒控制之休眠執行緒執行緒
- 【原創】Java多執行緒初學者指南(3):使用Runnable介面建立執行緒Java執行緒
- 使用Runnable介面實現執行緒的方法執行緒
- Java之實現多執行緒的方式三:實現Callable介面(結合執行緒池使用)Java執行緒
- 二. 執行緒管理之執行緒池執行緒
- Java多執行緒之執行緒中止Java執行緒
- 多執行緒系列之 執行緒安全執行緒
- iOS 多執行緒之執行緒安全iOS執行緒
- iOS多執行緒之執行緒安全iOS執行緒
- 瀏覽器多執行緒和js單執行緒瀏覽器執行緒JS
- java執行緒之守護執行緒和使用者執行緒Java執行緒
- Android多執行緒之執行緒池Android執行緒
- java多執行緒系列之執行緒池Java執行緒
- JAVA多執行緒Thread VS Runnable詳解Java執行緒thread
- 瀏覽器執行緒瀏覽器執行緒
- 多執行緒系列(十九) -Future使用詳解執行緒