比較java中Future與FutureTask之間的關(guān)系
Future與FutureTask都是用于獲取線(xiàn)程執(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方法用來(lái)取消任務(wù),取消成功則返回true,取消失敗則返回false。參數(shù)mayInterruptIfRunning設(shè)置為false,表示不允許在線(xiàn)程運(yùn)行時(shí)中斷,設(shè)置為true則表示允許。具體可分為以下三種情況:
1、如果任務(wù)已經(jīng)完成,則無(wú)論mayInterruptIfRunning為true還是false,都返回false,這是因?yàn)槟阋∠娜蝿?wù)已經(jīng)完成,則認(rèn)為取消任務(wù)失??;
2、如果任務(wù)正在執(zhí)行,則無(wú)論mayInterruptIfRunning為true還是false,都返回true。只不過(guò)mayInterruptIfRunning為true時(shí)線(xiàn)程會(huì)被中斷,false時(shí)線(xiàn)程不會(huì)被中斷會(huì)執(zhí)行完。
3、如果任務(wù)還沒(méi)有執(zhí)行,則無(wú)論mayInterruptIfRunning為true還是false,都返回true。
isCancelled方法用于判斷任務(wù)是否被取消成功,cancel方法成功則返回 true,反之則為false。
isDone用于判斷任務(wù)是否完成, 如果任務(wù)完成則返回true。任務(wù)完成包括正常結(jié)束、任務(wù)被取消、任務(wù)發(fā)生異常,都返回true
get()方法用來(lái)獲取執(zhí)行結(jié)果,這個(gè)方法會(huì)產(chǎn)生阻塞,會(huì)一直等到任務(wù)執(zhí)行完畢才返回;
get(long timeout, TimeUnit unit)用來(lái)獲取執(zhí)行結(jié)果,如果在指定時(shí)間內(nèi),還沒(méi)獲取到結(jié)果,拋出 java.util.concurrent.TimeoutException 異常
FutureTask 實(shí)現(xiàn)了RunnableFuture接口,而RunnableFuture則繼承了Future<V>與Runnable接口,所以 FutureTask不僅實(shí)現(xiàn)了 Future<V>接口的所有方法,還具有自己的run方法,我們可以看下它的類(lèi)圖
二、Future與FutureTask使用與分析
1、使用Future時(shí),我們需要實(shí)現(xiàn)Callable接口,并通過(guò)ExecutorService接口的submit方法獲取返回的Future對(duì)象,
2、使用FutureTask時(shí),根據(jù)FutureTask的構(gòu)造函數(shù)可以看到FutureTask既可以接收Callable的實(shí)現(xiàn)類(lèi),也可以接收Runnable的實(shí)現(xiàn)類(lèi)。當(dāng)你傳入的是Callable的實(shí)現(xiàn)類(lèi)時(shí),可以獲取線(xiàn)程執(zhí)行的結(jié)果;傳入Runnable的實(shí)現(xiàn)類(lèi)時(shí),由于Runnable的實(shí)現(xiàn)沒(méi)有返回值,需要傳入一個(gè)你設(shè)置的線(xiàn)程完成標(biāo)識(shí),也就是result,然后當(dāng)線(xiàn)程結(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 } }
接下來(lái)我們看下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"; // 線(xiàn)程運(yùn)行成功后把,返回你傳入的val值 FutureTask<String> futureTaskRun = new FutureTask<String>(taskRun, val); // 線(xiàn)程運(yùn)行,返回線(xiàn)程執(zhí)行的結(jié)果 FutureTask<String> futureTaskCall = new FutureTask<String>(taskCall); //聲明線(xiàn)程池 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自定義線(xiàn)程執(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類(lèi)對(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ì)通過(guò)newTaskFor方法轉(zhuǎn)換成FutureTask對(duì)象,所以我們具體分析下上面代碼中的三種情況:
1、如果你傳入的是自己實(shí)現(xiàn)的Runaable類(lèi)或者Callable類(lèi),那么sumbit方法自然會(huì)幫你自動(dòng)封裝為FutureTask對(duì)象,運(yùn)行后通過(guò)Future對(duì)象獲取結(jié)果。
2、你傳入的已經(jīng)是個(gè)自己構(gòu)造的FutureTask對(duì)象,由于FutureTask其實(shí)是實(shí)現(xiàn)了Runnable接口的,它本身就是個(gè)Runaable實(shí)現(xiàn)類(lèi), sumbit方法還是會(huì)將它視為Runnable類(lèi)來(lái)進(jìn)行封裝,并最終會(huì)執(zhí)行FutureTask自己的run方法,一系列實(shí)現(xiàn)都在你傳入的FutureTask對(duì)象內(nèi)完成,所以你可以直接通過(guò)自己構(gòu)建的FutureTask獲取結(jié)果;
3、自己?jiǎn)为?dú)聲明線(xiàn)程運(yùn)行,跟第2點(diǎn)類(lèi)似,F(xiàn)utureTask本身就是個(gè)Runnabel實(shí)現(xiàn)類(lèi),自然可以做為參數(shù)傳入Thread運(yùn)行;
那么我們把自定義的Runnable、Callable實(shí)現(xiàn)類(lèi)做為參數(shù)構(gòu)造FutureTask后,F(xiàn)uttureTask是如何運(yùn)行的呢,我們可以看下FuttureTask中具體的代碼實(shí)現(xiàn)
//你傳入的Runnable與Callable實(shí)現(xiàn)類(lèi)都會(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)類(lèi) 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類(lèi)本身的run方法,就是執(zhí)行Runnable、Callable的實(shí)現(xiàn)類(lèi)并獲取返回結(jié)果的過(guò)程。
所以ExecutorService接口中submit方法歸根結(jié)底還是要把你傳入的對(duì)象封裝成FutureTask對(duì)象,并通過(guò)FutureTask類(lèi)的內(nèi)部實(shí)現(xiàn)來(lái)獲取結(jié)果的,返回的Future接口對(duì)象也要依賴(lài)于FutureTask實(shí)例化的,所以無(wú)論是直接傳入自己的Runnable、Callable實(shí)現(xiàn)類(lèi)還是構(gòu)建FutureTask傳入,本質(zhì)上都是通過(guò)FutureTask去實(shí)現(xiàn),沒(méi)有什么區(qū)別;
相關(guān)文章
IDEA中thymeleaf語(yǔ)法沒(méi)有提示的問(wèn)題及解決
這篇文章主要介紹了IDEA中thymeleaf語(yǔ)法沒(méi)有提示的問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05JAVA面試題之緩存擊穿、緩存穿透、緩存雪崩的三者區(qū)別
當(dāng)服務(wù)器QPS比較高,并且對(duì)數(shù)據(jù)的實(shí)時(shí)性要求不高時(shí),往往會(huì)接入緩存以達(dá)到快速Response、降低數(shù)據(jù)庫(kù)壓力的作用,常用來(lái)做緩存的中間件如Redis等。本文主要介紹了JAVA面試時(shí)常考的緩存擊穿、穿透、雪崩場(chǎng)景三者區(qū)別,有興趣的小伙伴可以看一下2021-11-11Sa-Token不同模式實(shí)現(xiàn)單地登錄?多地登錄?同端互斥登錄
這篇文章主要為大家介紹了Sa-Token不同模式實(shí)現(xiàn)單地登錄?多地登錄?同端互斥登錄,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07解析springboot整合谷歌開(kāi)源緩存框架Guava Cache原理
本文主要為大家解析了springboot整合谷歌開(kāi)源緩存框架Guava Cache的原理以及在實(shí)際開(kāi)發(fā)過(guò)程中的使用,附含源碼,有需要的朋友可以參考下2021-08-08Spring?BeanPostProcessor后處理器源碼解析
這篇文章主要介紹了Spring?BeanPostProcessor后處理器源碼解析,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-09-09Springboot把外部依賴(lài)包納入Spring容器管理的兩種方式
這篇文章主要給大家介紹了Springboot把外部依賴(lài)包納入Spring容器管理的兩種方式,Spring.factories和org.springframework.boot.autoconfigure.AutoConfiguration.imports,有感興趣的小伙伴可以參考閱讀本文2023-07-07Android開(kāi)發(fā)中實(shí)現(xiàn)用戶(hù)注冊(cè)和登陸的代碼實(shí)例分享
這篇文章主要介紹了Android開(kāi)發(fā)中實(shí)現(xiàn)用戶(hù)注冊(cè)和登陸的代碼實(shí)例分享,只是實(shí)現(xiàn)基本功能,界面華麗度就請(qǐng)忽略啦XD 需要的朋友可以參考下2015-12-12解決mybatis-plus 查詢(xún)耗時(shí)慢的問(wèn)題
這篇文章主要介紹了解決mybatis-plus 查詢(xún)耗時(shí)慢的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07