比較java中Future與FutureTask之間的關(guān)系
Future與FutureTask都是用于獲取線程執(zhí)行的返回結(jié)果。下面我們就對(duì)兩者之間的關(guān)系與使用進(jìn)行一個(gè)大致的介紹與分析
一、Future與FutureTask介紹:
Future位于java.util.concurrent包下,它是一個(gè)接口
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;
}
Future接口中聲明了5個(gè)方法,下面介紹一下每個(gè)方法的作用:
cancel方法用來取消任務(wù),取消成功則返回true,取消失敗則返回false。參數(shù)mayInterruptIfRunning設(shè)置為false,表示不允許在線程運(yùn)行時(shí)中斷,設(shè)置為true則表示允許。具體可分為以下三種情況:
1、如果任務(wù)已經(jīng)完成,則無論mayInterruptIfRunning為true還是false,都返回false,這是因?yàn)槟阋∠娜蝿?wù)已經(jīng)完成,則認(rèn)為取消任務(wù)失??;
2、如果任務(wù)正在執(zhí)行,則無論mayInterruptIfRunning為true還是false,都返回true。只不過mayInterruptIfRunning為true時(shí)線程會(huì)被中斷,false時(shí)線程不會(huì)被中斷會(huì)執(zhí)行完。
3、如果任務(wù)還沒有執(zhí)行,則無論mayInterruptIfRunning為true還是false,都返回true。
isCancelled方法用于判斷任務(wù)是否被取消成功,cancel方法成功則返回 true,反之則為false。
isDone用于判斷任務(wù)是否完成, 如果任務(wù)完成則返回true。任務(wù)完成包括正常結(jié)束、任務(wù)被取消、任務(wù)發(fā)生異常,都返回true
get()方法用來獲取執(zhí)行結(jié)果,這個(gè)方法會(huì)產(chǎn)生阻塞,會(huì)一直等到任務(wù)執(zhí)行完畢才返回;
get(long timeout, TimeUnit unit)用來獲取執(zhí)行結(jié)果,如果在指定時(shí)間內(nèi),還沒獲取到結(jié)果,拋出 java.util.concurrent.TimeoutException 異常
FutureTask 實(shí)現(xiàn)了RunnableFuture接口,而RunnableFuture則繼承了Future<V>與Runnable接口,所以 FutureTask不僅實(shí)現(xiàn)了 Future<V>接口的所有方法,還具有自己的run方法,我們可以看下它的類圖

二、Future與FutureTask使用與分析
1、使用Future時(shí),我們需要實(shí)現(xiàn)Callable接口,并通過ExecutorService接口的submit方法獲取返回的Future對(duì)象,
2、使用FutureTask時(shí),根據(jù)FutureTask的構(gòu)造函數(shù)可以看到FutureTask既可以接收Callable的實(shí)現(xiàn)類,也可以接收Runnable的實(shí)現(xiàn)類。當(dāng)你傳入的是Callable的實(shí)現(xiàn)類時(shí),可以獲取線程執(zhí)行的結(jié)果;傳入Runnable的實(shí)現(xiàn)類時(shí),由于Runnable的實(shí)現(xiàn)沒有返回值,需要傳入一個(gè)你設(shè)置的線程完成標(biāo)識(shí),也就是result,然后當(dāng)線程結(jié)束時(shí)會(huì)把你傳入的result原值返回給你,F(xiàn)utureTask的構(gòu)造函數(shù)具體如下:
public class FutureTask<V> implements RunnableFuture<V>{
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);//runnable轉(zhuǎn)化為callable
this.state = NEW; // ensure visibility of callable
}
}
接下來我們看下Future與FutureTask具體的使用代碼:
// 執(zhí)行任務(wù) 實(shí)現(xiàn)Runnable FutureTaskJobRunnable taskRun = new FutureTaskJobRunnable(); // 執(zhí)行任務(wù) 實(shí)現(xiàn)Callable FutureTaskJobCallable taskCall = new FutureTaskJobCallable(); String val = "ok"; // 線程運(yùn)行成功后把,返回你傳入的val值 FutureTask<String> futureTaskRun = new FutureTask<String>(taskRun, val); // 線程運(yùn)行,返回線程執(zhí)行的結(jié)果 FutureTask<String> futureTaskCall = new FutureTask<String>(taskCall); //聲明線程池 ExecutorService executor = Executors.newCachedThreadPool(); //Future Future<String> future = executor.submit(taskCall); System.out.println(future.get()); //FutureTask executor.submit(futureTaskCall); System.out.println(futureTaskCall.get()); //FutureTask自定義線程執(zhí)行 new Thread(futureTaskRun).start(); System.out.println(futureTaskRun.get());
public class FutureTaskJobCallable implements Callable<String>{
public String call() throws Exception {
System.out.println("FutureTaskJobCallable已經(jīng)執(zhí)行了哦");
Thread.sleep(1000);
return "返回結(jié)果";
}
}
public class FutureTaskJobRunnable implements Runnable {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("FutureTaskJobRunnable已經(jīng)執(zhí)行了哦");
}
}
根據(jù)上面的代碼我們從ExecutorService接口中submit方法入手,看下AbstractExecutorService類對(duì)submit方法的具體實(shí)現(xiàn)。
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;
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
可以看到當(dāng)你使用submit方法提交任務(wù)時(shí),都會(huì)通過newTaskFor方法轉(zhuǎn)換成FutureTask對(duì)象,所以我們具體分析下上面代碼中的三種情況:
1、如果你傳入的是自己實(shí)現(xiàn)的Runaable類或者Callable類,那么sumbit方法自然會(huì)幫你自動(dòng)封裝為FutureTask對(duì)象,運(yùn)行后通過Future對(duì)象獲取結(jié)果。
2、你傳入的已經(jīng)是個(gè)自己構(gòu)造的FutureTask對(duì)象,由于FutureTask其實(shí)是實(shí)現(xiàn)了Runnable接口的,它本身就是個(gè)Runaable實(shí)現(xiàn)類, sumbit方法還是會(huì)將它視為Runnable類來進(jìn)行封裝,并最終會(huì)執(zhí)行FutureTask自己的run方法,一系列實(shí)現(xiàn)都在你傳入的FutureTask對(duì)象內(nèi)完成,所以你可以直接通過自己構(gòu)建的FutureTask獲取結(jié)果;
3、自己單獨(dú)聲明線程運(yùn)行,跟第2點(diǎn)類似,F(xiàn)utureTask本身就是個(gè)Runnabel實(shí)現(xiàn)類,自然可以做為參數(shù)傳入Thread運(yùn)行;
那么我們把自定義的Runnable、Callable實(shí)現(xiàn)類做為參數(shù)構(gòu)造FutureTask后,F(xiàn)uttureTask是如何運(yùn)行的呢,我們可以看下FuttureTask中具體的代碼實(shí)現(xiàn)
//你傳入的Runnable與Callable實(shí)現(xiàn)類都會(huì)在構(gòu)造函數(shù)中轉(zhuǎn)化為Callable
private Callable<V> callable;
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;//你傳入的實(shí)現(xiàn)類
if (c != null && state == NEW) {
V result;//返回值
boolean ran;
try {
result = c.call();//運(yùn)行后返回結(jié)果
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
可以看到FutureTask類本身的run方法,就是執(zhí)行Runnable、Callable的實(shí)現(xiàn)類并獲取返回結(jié)果的過程。
所以ExecutorService接口中submit方法歸根結(jié)底還是要把你傳入的對(duì)象封裝成FutureTask對(duì)象,并通過FutureTask類的內(nèi)部實(shí)現(xiàn)來獲取結(jié)果的,返回的Future接口對(duì)象也要依賴于FutureTask實(shí)例化的,所以無論是直接傳入自己的Runnable、Callable實(shí)現(xiàn)類還是構(gòu)建FutureTask傳入,本質(zhì)上都是通過FutureTask去實(shí)現(xiàn),沒有什么區(qū)別;
相關(guān)文章
CountDownLatch源碼解析之countDown()
這篇文章主要為大家詳細(xì)解析了CountDownLatch源碼之countDown方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04
Java擴(kuò)展庫RxJava的基本結(jié)構(gòu)與適用場景小結(jié)
RxJava(GitHub: https://github.com/ReactiveX/RxJava)能夠幫助Java進(jìn)行異步與事務(wù)驅(qū)動(dòng)的程序編寫,這里我們來作一個(gè)Java擴(kuò)展庫RxJava的基本結(jié)構(gòu)與適用場景小結(jié),剛接觸RxJava的同學(xué)不妨看一下^^2016-06-06
SpringCloud Alibaba微服務(wù)實(shí)戰(zhàn)之遠(yuǎn)程Feign請(qǐng)求頭丟失問題解決方案
這篇文章主要介紹了SpringCloud Alibaba微服務(wù)實(shí)戰(zhàn)之遠(yuǎn)程Feign請(qǐng)求頭丟失問題,對(duì)SpringCloud Alibaba Feign請(qǐng)求頭問題感興趣的朋友跟隨小編一起看看吧2024-02-02
利用ssh實(shí)現(xiàn)服務(wù)器文件上傳下載
這篇文章主要為大家詳細(xì)介紹了如何利用ssh實(shí)現(xiàn)服務(wù)器文件上傳下載,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
SpringBoot使用Nacos進(jìn)行application.yml配置管理詳解
Nacos是阿里巴巴開源的一個(gè)微服務(wù)配置管理和服務(wù)發(fā)現(xiàn)的解決方案,下面我們來看看在SpringBoot中如何使用Nacos進(jìn)行application.yml配置管理吧2025-03-03
2024版本IDEA創(chuàng)建Servlet模板的圖文教程
新版IDEA?2024.1.4中,用戶需要自行創(chuàng)建Servlet模板以解決Web項(xiàng)目無法通過右鍵創(chuàng)建Servlet的問題,本文詳細(xì)介紹了添加ServletAnnotatedClass.java模板的步驟,幫助用戶快速配置并使用新的Servlet模板,需要的朋友可以參考下2024-10-10
EventBus與Spring Event區(qū)別詳解(EventBus 事件機(jī)制,Spring Event事件機(jī)制)
這篇文章主要介紹了EventBus與Spring Event區(qū)別,需要的朋友可以參考下2020-02-02
Java實(shí)現(xiàn)瀏覽器大文件上傳的示例詳解
文件上傳是許多項(xiàng)目都有的功能,用戶上傳小文件速度一般都很快,但如果是大文件幾個(gè)g,幾十個(gè)g的時(shí)候,上傳了半天,馬上就要完成的時(shí)候,網(wǎng)絡(luò)波動(dòng)一下,文件又要重新上傳,所以本文給大家介紹了Java實(shí)現(xiàn)瀏覽器大文件上傳的示例,需要的朋友可以參考下2024-07-07

