摘要:聲明了幾種方法,其中有一個就是傳入聲明了對具體的或者任務執行進行取消查詢結果獲取等方法。事實上,是接口的一個唯一實現類。使用示例第一種方式是使用繼承了的線程池中的方法,將直接提交創建。
創建線程的兩種方式
直接繼承 Thread
實現 Runnable 接口
這兩種方式都有一個缺點:在執行完成任務之后,無法直接獲取到最后的執行結果。如果需要獲取執行結果,就必須通過共享變量或線程通信的方式來達到想要的效果,較為麻煩。
所以從 Java 1.5 起,就提供了兩種方式:Callable 和 Future,通過它們可以在任務執行結束后得到任務執行結果。
Runnable 與 Callable首先是 java.lang.Rannable,它是一個接口,里面只聲明了一個 run() 方法:
@FunctionalInterface public interface Runnable { public abstract void run(); }
由于 run() 方法的返回值類型為 void,所以在線程執行完后無任何返回結果。
然后是 java.util.concurrent.Callable,它也是一個接口,也只聲明了一個方法,其名為 call():
@FunctionalInterface public interface Callable{ V call() throws Exception; }
可以看到,這是一個泛型接口,返回的類型就是傳進來的 V 類型。
那么,如何使用 Callable 呢?一般是結合 ExecutorService 來使用。ExecutorService 聲明了幾種 submit 方法,其中有一個就是傳入 Callable:
FutureFuture submit(Callable task);
Future 聲明了對具體的 Runnable 或者 Callable 任務執行進行取消、查詢、結果獲取等方法。必要時可以通過 get 方法獲取執行結果,該方法會阻塞直到任務返回結果。
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 提供了三種功能:
取消任務:參數表示是否允許中途取消(中斷)
判斷狀態:是否已取消、是否已完成
獲取結果:兩種方式,指不指定時間
因為 Future 只是一個接口,所以無法直接用來創建對象,因此有了下面的 FutureTask。
FutureTask首先看下 FutureTask 的繼承關系:
可以看出 RunnableFuture 繼承了 Runnable 接口和 Future 接口,而 FutureTask 實現了 RunnableFuture 接口。所以它既可以作為 Runnable 被線程執行,又可以作為 Future 得到 Callable 的返回值。
然后可以看到 FutureTask 內部有這幾種狀態:
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;
再根據注釋,可以得知當創建一個 FutureTask 對象時,初始狀態是 NEW,在運行過程中,運行狀態僅在方法set,setException 和 cancel 中轉換為終端狀態。有四種狀態轉換過程:
NEW -> COMPLETING -> NORMAL:正常執行并返回結果(run 執行成功再設置狀態為 COMPLETING)
NEW -> COMPLETING -> EXCEPTIONAL:執行過程中出現異常(setException 先設置狀態為 COMPLETING)
NEW -> CANCELLED:執行前被取消
NEW -> INTERRUPTING -> INTERRUPTED:執行時被中斷(cancel 參數為 true 才可能出現這個狀態)
最后來看下 FutureTask 的兩個構造器:
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 }
可知傳入的任務最后都是付給內部的 private Callable
事實上,FutureTask 是 Future 接口的一個唯一實現類。
更多關于 FutureTask 的源碼分析,可以看 FutureTask源碼解析 這篇文章。使用示例 Future
第一種方式是使用繼承了 ExecutorService 的線程池 ThreadPoolExecutor 中的 submit 方法,將 Callable 直接提交創建 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); } }
第二種方法是創建一個寫好 Callable 的 FutureTask 對象實例,再 submit。因為 FutureTask 內部本身擁有 run 方法,也可以直接創建線程 Thread 運行。
利用 ExecutorService
import java.util.concurrent.* public class FutureTaskWithExecutorService { public static void main(String[] args) throws InterruptedException, ExecutionException { FutureTaskfutureTask = new FutureTask<>(() -> { // java 8 函數式寫法 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并發編程:Callable、Future和FutureTask
FutureTask源碼解析
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/76857.html
摘要:線程的啟動與銷毀都與本地線程同步。操作系統會調度所有線程并將它們分配給可用的。框架的成員主要成員線程池接口接口接口以及工具類。創建單個線程的接口與其實現類用于表示異步計算的結果。參考書籍并發編程的藝術方騰飛魏鵬程曉明著 在java中,直接使用線程來異步的執行任務,線程的每次創建與銷毀需要一定的計算機資源開銷。每個任務創建一個線程的話,當任務數量多的時候,則對應的創建銷毀開銷會消耗大量...
摘要:目標線程由運行狀態轉換為就緒狀態,也就是讓出執行權限,讓其他線程得以優先執行,但其他線程能否優先執行時未知的。函數的官方解釋是意思是使調用該函數的線程讓出執行時間給其他已就緒狀態的線程。 線程允許在同一個進程中同時存在多個程序控制流,即通過線程可以實現同時處理多個任務的功能。線程會共享進程范圍內的資源,例如內存句柄和文件句柄,但每個線程都有各自的程序計數器、棧以及局部變量。 多線程的實...
摘要:一個線程池包含很多準備運行的空閑線程,每當執行完畢后,線程不會死亡而是回到線程池準備為下一個請求提供服務。另一個使用線程池的理由是減少并發線程數。創建大量線程會大大降低性能甚至拖垮虛擬機。 【Future的概念 interface Future ,表示異步計算的結果,Future有個get方法而獲取結果只有在計算完成時獲取,否則會一直阻塞直到任務轉入完成狀態,然后會返回結果或者拋出異常...
摘要:一使用線程池的好處線程池提供了一種限制和管理資源包括執行一個任務。每個線程池還維護一些基本統計信息,例如已完成任務的數量。通過重復利用已創建的線程降低線程創建和銷毀造成的消耗。使用無界隊列作為線程池的工作隊列會對線程池帶來的影響與相同。 歷史優質文章推薦: Java并發編程指南專欄 分布式系統的經典基礎理論 可能是最漂亮的Spring事務管理詳解 面試中關于Java虛擬機(jvm)的問...
閱讀 2288·2019-08-30 15:56
閱讀 3120·2019-08-30 13:48
閱讀 1134·2019-08-30 10:52
閱讀 1505·2019-08-29 17:30
閱讀 431·2019-08-29 13:44
閱讀 3566·2019-08-29 12:53
閱讀 1127·2019-08-29 11:05
閱讀 2680·2019-08-26 13:24