摘要:聲明了幾種方法,其中有一個(gè)就是傳入聲明了對(duì)具體的或者任務(wù)執(zhí)行進(jìn)行取消查詢結(jié)果獲取等方法。事實(shí)上,是接口的一個(gè)唯一實(shí)現(xiàn)類。使用示例第一種方式是使用繼承了的線程池中的方法,將直接提交創(chuàng)建。
創(chuàng)建線程的兩種方式
直接繼承 Thread
實(shí)現(xiàn) Runnable 接口
這兩種方式都有一個(gè)缺點(diǎn):在執(zhí)行完成任務(wù)之后,無法直接獲取到最后的執(zhí)行結(jié)果。如果需要獲取執(zhí)行結(jié)果,就必須通過共享變量或線程通信的方式來達(dá)到想要的效果,較為麻煩。
所以從 Java 1.5 起,就提供了兩種方式:Callable 和 Future,通過它們可以在任務(wù)執(zhí)行結(jié)束后得到任務(wù)執(zhí)行結(jié)果。
Runnable 與 Callable首先是 java.lang.Rannable,它是一個(gè)接口,里面只聲明了一個(gè) run() 方法:
@FunctionalInterface public interface Runnable { public abstract void run(); }
由于 run() 方法的返回值類型為 void,所以在線程執(zhí)行完后無任何返回結(jié)果。
然后是 java.util.concurrent.Callable,它也是一個(gè)接口,也只聲明了一個(gè)方法,其名為 call():
@FunctionalInterface public interface Callable{ V call() throws Exception; }
可以看到,這是一個(gè)泛型接口,返回的類型就是傳進(jìn)來的 V 類型。
那么,如何使用 Callable 呢?一般是結(jié)合 ExecutorService 來使用。ExecutorService 聲明了幾種 submit 方法,其中有一個(gè)就是傳入 Callable:
FutureFuture submit(Callable task);
Future 聲明了對(duì)具體的 Runnable 或者 Callable 任務(wù)執(zhí)行進(jìn)行取消、查詢、結(jié)果獲取等方法。必要時(shí)可以通過 get 方法獲取執(zhí)行結(jié)果,該方法會(huì)阻塞直到任務(wù)返回結(jié)果。
public interface Future{ boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
由此可見,Future 提供了三種功能:
取消任務(wù):參數(shù)表示是否允許中途取消(中斷)
判斷狀態(tài):是否已取消、是否已完成
獲取結(jié)果:兩種方式,指不指定時(shí)間
因?yàn)?Future 只是一個(gè)接口,所以無法直接用來創(chuàng)建對(duì)象,因此有了下面的 FutureTask。
FutureTask首先看下 FutureTask 的繼承關(guān)系:
可以看出 RunnableFuture 繼承了 Runnable 接口和 Future 接口,而 FutureTask 實(shí)現(xiàn)了 RunnableFuture 接口。所以它既可以作為 Runnable 被線程執(zhí)行,又可以作為 Future 得到 Callable 的返回值。
然后可以看到 FutureTask 內(nèi)部有這幾種狀態(tài):
private volatile int state; private static final int NEW = 0; private static final int COMPLETING = 1; private static final int NORMAL = 2; private static final int EXCEPTIONAL = 3; private static final int CANCELLED = 4; private static final int INTERRUPTING = 5; private static final int INTERRUPTED = 6;
再根據(jù)注釋,可以得知當(dāng)創(chuàng)建一個(gè) FutureTask 對(duì)象時(shí),初始狀態(tài)是 NEW,在運(yùn)行過程中,運(yùn)行狀態(tài)僅在方法set,setException 和 cancel 中轉(zhuǎn)換為終端狀態(tài)。有四種狀態(tài)轉(zhuǎn)換過程:
NEW -> COMPLETING -> NORMAL:正常執(zhí)行并返回結(jié)果(run 執(zhí)行成功再設(shè)置狀態(tài)為 COMPLETING)
NEW -> COMPLETING -> EXCEPTIONAL:執(zhí)行過程中出現(xiàn)異常(setException 先設(shè)置狀態(tài)為 COMPLETING)
NEW -> CANCELLED:執(zhí)行前被取消
NEW -> INTERRUPTING -> INTERRUPTED:執(zhí)行時(shí)被中斷(cancel 參數(shù)為 true 才可能出現(xiàn)這個(gè)狀態(tài))
最后來看下 FutureTask 的兩個(gè)構(gòu)造器:
public FutureTask(Callablecallable) { 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); this.state = NEW; // ensure visibility of callable }
可知傳入的任務(wù)最后都是付給內(nèi)部的 private Callable
事實(shí)上,FutureTask 是 Future 接口的一個(gè)唯一實(shí)現(xiàn)類。
更多關(guān)于 FutureTask 的源碼分析,可以看 FutureTask源碼解析 這篇文章。使用示例 Future
第一種方式是使用繼承了 ExecutorService 的線程池 ThreadPoolExecutor 中的 submit 方法,將 Callable 直接提交創(chuàng)建 Future。
import java.util.concurrent.*; public class FutureExample { static class MyCallable implements CallableFutureTask{ @Override public String call() throws Exception { System.out.println("do something in callable"); Thread.sleep(5000); return "Ok"; } } public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newCachedThreadPool(); Future future = executorService.submit(new MyCallable()); System.out.println("do something in main"); Thread.sleep(1000); String result = future.get(); System.out.println("result: " + result); } }
第二種方法是創(chuàng)建一個(gè)寫好 Callable 的 FutureTask 對(duì)象實(shí)例,再 submit。因?yàn)?FutureTask 內(nèi)部本身?yè)碛?run 方法,也可以直接創(chuàng)建線程 Thread 運(yùn)行。
利用 ExecutorService
import java.util.concurrent.* public class FutureTaskWithExecutorService { public static void main(String[] args) throws InterruptedException, ExecutionException { FutureTaskfutureTask = new FutureTask<>(() -> { // java 8 函數(shù)式寫法 System.out.println("do something in callable"); Thread.sleep(5000); return "Ok"; }); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.submit(futureTask); System.out.println("do something in main"); Thread.sleep(1000); String result = futureTask.get(); System.out.println("result: " + result); } }
利用 Thread
import java.util.concurrent.* public class FutureTaskWithThread { public static void main(String[] args) throws InterruptedException, ExecutionException { FutureTask參考資料futureTask = new FutureTask<>(new Callable () { @Override public String call() throws Exception { System.out.println("do something in callable"); Thread.sleep(5000); return "Ok"; } }); new Thread(futureTask).start(); System.out.println("do something in main"); Thread.sleep(1000); String result = futureTask.get(); System.out.println("result: " + result); } }
Java并發(fā)編程:Callable、Future和FutureTask
FutureTask源碼解析
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/76857.html
摘要:線程的啟動(dòng)與銷毀都與本地線程同步。操作系統(tǒng)會(huì)調(diào)度所有線程并將它們分配給可用的。框架的成員主要成員線程池接口接口接口以及工具類。創(chuàng)建單個(gè)線程的接口與其實(shí)現(xiàn)類用于表示異步計(jì)算的結(jié)果。參考書籍并發(fā)編程的藝術(shù)方騰飛魏鵬程曉明著 在java中,直接使用線程來異步的執(zhí)行任務(wù),線程的每次創(chuàng)建與銷毀需要一定的計(jì)算機(jī)資源開銷。每個(gè)任務(wù)創(chuàng)建一個(gè)線程的話,當(dāng)任務(wù)數(shù)量多的時(shí)候,則對(duì)應(yīng)的創(chuàng)建銷毀開銷會(huì)消耗大量...
摘要:目標(biāo)線程由運(yùn)行狀態(tài)轉(zhuǎn)換為就緒狀態(tài),也就是讓出執(zhí)行權(quán)限,讓其他線程得以優(yōu)先執(zhí)行,但其他線程能否優(yōu)先執(zhí)行時(shí)未知的。函數(shù)的官方解釋是意思是使調(diào)用該函數(shù)的線程讓出執(zhí)行時(shí)間給其他已就緒狀態(tài)的線程。 線程允許在同一個(gè)進(jìn)程中同時(shí)存在多個(gè)程序控制流,即通過線程可以實(shí)現(xiàn)同時(shí)處理多個(gè)任務(wù)的功能。線程會(huì)共享進(jìn)程范圍內(nèi)的資源,例如內(nèi)存句柄和文件句柄,但每個(gè)線程都有各自的程序計(jì)數(shù)器、棧以及局部變量。 多線程的實(shí)...
摘要:一個(gè)線程池包含很多準(zhǔn)備運(yùn)行的空閑線程,每當(dāng)執(zhí)行完畢后,線程不會(huì)死亡而是回到線程池準(zhǔn)備為下一個(gè)請(qǐng)求提供服務(wù)。另一個(gè)使用線程池的理由是減少并發(fā)線程數(shù)。創(chuàng)建大量線程會(huì)大大降低性能甚至拖垮虛擬機(jī)。 【Future的概念 interface Future ,表示異步計(jì)算的結(jié)果,F(xiàn)uture有個(gè)get方法而獲取結(jié)果只有在計(jì)算完成時(shí)獲取,否則會(huì)一直阻塞直到任務(wù)轉(zhuǎn)入完成狀態(tài),然后會(huì)返回結(jié)果或者拋出異常...
摘要:一使用線程池的好處線程池提供了一種限制和管理資源包括執(zhí)行一個(gè)任務(wù)。每個(gè)線程池還維護(hù)一些基本統(tǒng)計(jì)信息,例如已完成任務(wù)的數(shù)量。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。使用無界隊(duì)列作為線程池的工作隊(duì)列會(huì)對(duì)線程池帶來的影響與相同。 歷史優(yōu)質(zhì)文章推薦: Java并發(fā)編程指南專欄 分布式系統(tǒng)的經(jīng)典基礎(chǔ)理論 可能是最漂亮的Spring事務(wù)管理詳解 面試中關(guān)于Java虛擬機(jī)(jvm)的問...
閱讀 2270·2019-08-30 15:56
閱讀 3108·2019-08-30 13:48
閱讀 1123·2019-08-30 10:52
閱讀 1490·2019-08-29 17:30
閱讀 417·2019-08-29 13:44
閱讀 3528·2019-08-29 12:53
閱讀 1113·2019-08-29 11:05
閱讀 2667·2019-08-26 13:24