摘要:可取消的異步計算。只有在計算完成后才能檢索結果如果計算還沒有完成,方法將會被阻塞。任務正常執行結束。任務執行過程中發生異常。任務即將被中斷。運行完成后將會清空。根據執行結果設置狀態。
FutureTask What is it
? 可取消的異步計算。該類提供了 Future的基本實現,其中包括啟動和取消計算的方法,查詢計算是否完成以及檢索計算結果的方法。只有在計算完成后才能檢索結果;如果計算還沒有完成,{getcode}方法將會被阻塞。一旦計算完成,計算不能被重新啟動或取消(除非計算是使用調用的runAndReset()。
? 該類實現自RunableFuture接口,其中RunableFuture接口又繼承自Runable和Future。所以可以理解為:FutureTask是一個可以計算Future結果的一個Future實現,
How to use由于FutureTask間接或直接實現了Runable和Future接口,所以其具有如下特征:
可以像一個普通的任務一樣,使用線程池提交一個任務并執行。
ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.submit(new FutureTask(new Callable () { @Override public Integer call() throws Exception { return 100; } }));
可以像一個普通的任務一樣,使用Thread來執行,但可以異步獲取結果。
FutureTask futureTask = new FutureTaskWhen to use(new Callable () { @Override public Integer call() throws Exception { return 100; } }); new Thread(futureTask).start(); futureTask.get();
考慮一種使用Cache的場景:一般情況下,對于熱點數據我們都會使用cache保存數據,只有當cache失效了,才會進行耗時的網絡調用或者數據庫查詢。但是當cache失效時,同時有多個該key的查詢,那么在短時間內可能會有多個相同的耗時查詢,瞬間對系統性能會有一定的損失,為了解決這種情況可以采取緩存FutureTask的方式解決:
思路借鑒:https://github.com/javacreed/...
//獲取緩存的客戶端 public class CacheClient { public staticHow to designT getCache(int id){ return null; } } //Service層邏輯 public class CacheService { private static ConcurrentMap > cacheFuture = new ConcurrentHashMap<>(); public User getUserInfo(int id) { Future future = createFutureIfAbsent(id); try { return future.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } return null; } private Future createFutureIfAbsent(final int id) { Future future = cacheFuture.get(id); if (future == null) { FutureTask futureTask = new FutureTask (new Callable () { @Override public User call() throws Exception { return CacheClient.getCache(id); } }); future = cacheFuture.putIfAbsent(id, futureTask); if (future == null) { future = futureTask; futureTask.run(); } } return future; } public class User { private int id; private String name; private String age; 。。。 } }
? FutureTask作為一個可運行的Future,其運行過程中存在狀態的遷移過程,FutureTask的運行狀態有:
NEW:初始狀態。
COMPLETING:結果正在被set過程中。
NORMAL:任務正常執行結束。
EXCEPTIONAL:任務執行過程中發生異常。
CANCELLED:任務執行過程中被取消。
INTERRUPTING:任務即將被中斷。
INTERRUPTED:任務已經被中斷。
狀態躍遷:
正常結束:NEW->COMPLETING->NORMAL
出現異常:NEW->COMPLETING->EXCEPTIONAL
任務被取消且不響應中斷:NEW->CANCELLED
任務被取消且響應中斷:NEW->INTERRUPTING->INTERRUPTED
state:指示當前任務執行的狀態。
callback:需要被運行的任務。運行完成后將會清空。
outcome:保存任務執行之后的結果。
runner:持有任務執行過程中運行線程。
waiters:等待線程的堆棧[稍后將做詳細分析]。
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 }
FutureTask有兩個構造方法,雖然兩個構造方法的入參略有不同,但是在底層執行時都是按照Callback任務來構建的。并在此過程初始化當前的任務狀態為:NEW
下面將從核心方法開始,逐漸分析FutureTask的原理:
run():任務執行
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callablec = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { runner = null; int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
該方法的邏輯很簡單,主要完成了如下任務:
1.首先判斷任務的有效性:1)該任務的狀態是否為初始狀態:NEW,2)把運行任務的線程設置給成員變量runner。
2.執行任務。
3.根據執行結果設置狀態。
get()/get(long timeout, TimeUnit unit)
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (unit == null) throw new NullPointerException(); int s = state; if (s <= COMPLETING && (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING) throw new TimeoutException(); return report(s); }```
該方法的邏輯更簡單:首先判斷當前的狀態,然后就會調用awaitDone()方法等待結果,當等待超時就會拋出TimeOutException,否則調用report()將結果報告出去。下面看看等待結果是如何處理的:
private int awaitDone(boolean timed, long nanos) throws InterruptedException { //首先計算出該任務的最終結束時間 final long deadline = timed ? System.nanoTime() + nanos : 0L; WaitNode q = null; boolean queued = false; for (;;) { //判斷當前線程是否被中斷 if (Thread.interrupted()) { //從等待隊列中刪除該線程的等待節點 removeWaiter(q); throw new InterruptedException(); } int s = state; //如果狀態>COMPLETING,說明任務已經結束了,不管是否正常結束,都是可以返回的 if (s > COMPLETING) { if (q != null) q.thread = null; return s; } //如果當前狀態還是COMPLETING,說明結果來沒有返回呢,那就讓出CPU else if (s == COMPLETING) // cannot time out yet Thread.yield(); //如果當前任務還沒有生成等待節點,那么就創建一個以當前線程的等待節點。 else if (q == null) q = new WaitNode(); else if (!queued) //采用頭插法構建等待隊列 queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q); else if (timed) { nanos = deadline - System.nanoTime(); //任務執行超時了,那么就刪除等待隊列 if (nanos <= 0L) { removeWaiter(q); return state; } //還沒有超時,那么就將當前線程park LockSupport.parkNanos(this, nanos); } else LockSupport.park(this); }
該方法雖然篇幅很大,但是完成的任務也是很簡單的,主要可以總結如下:
首先判斷在超時時間內,任務是否執行完成(失敗)。
通過狀態為判斷任務是否執行完成或失敗。
? NOTE:為什么要使用這個waiter?[多帶帶文章分析:]
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/68154.html
摘要:零前期準備文章異常啰嗦且繞彎。版本版本簡介是中默認的實現類,常與結合進行多線程并發操作。所以方法的主體其實就是去喚醒被阻塞的線程。本文僅為個人的學習筆記,可能存在錯誤或者表述不清的地方,有緣補充 零 前期準備 0 FBI WARNING 文章異常啰嗦且繞彎。 1 版本 JDK 版本 : OpenJDK 11.0.1 IDE : idea 2018.3 2 ThreadLocal 簡介 ...
摘要:聲明了幾種方法,其中有一個就是傳入聲明了對具體的或者任務執行進行取消查詢結果獲取等方法。事實上,是接口的一個唯一實現類。使用示例第一種方式是使用繼承了的線程池中的方法,將直接提交創建。 創建線程的兩種方式 直接繼承 Thread 實現 Runnable 接口 這兩種方式都有一個缺點:在執行完成任務之后,無法直接獲取到最后的執行結果。如果需要獲取執行結果,就必須通過共享變量或線程通...
摘要:類提供了一些有用的方法在線程池中執行內的任務。在線程池提交任務后返回了一個對象,使用它可以知道任務的狀態和得到返回的執行結果。 Callable和Future出現的原因 創建線程的2種方式,一種是直接繼承Thread,另外一種就是實現Runnable接口。 這2種方式都有一個缺陷就是:在執行完任務之后無法獲取執行結果。 如果需要獲取執行結果,就必須通過共享變量或者使用線程通信的方式來達...
摘要:背景通過接口實現調用發送數據,接口返回值為發送數據的對應結果。接口為同步阻塞,為異步回調方式。接收數據回調接收到數據后,通過閉鎖釋放阻塞的線程,同時設置結果返回給調用者 背景 通過HTTP接口實現調用MQTT Client發送數據,HTTP接口返回值為MQTT Client發送數據的對應結果。 HTTP接口為同步阻塞,MQTT Client 為異步回調方式。如何實現在HTTP接口中調用...
摘要:在并發編程學習之三種線程啟動方式中有提過。是否執行結束,包括正常執行結束或異常結束。獲取返回值,沒有得到返回值前一直阻塞。運行結果如下由于任務被取消,所以拋出異常。注意的是,此時線程還在跑,和返回的是。并不能讓任務真正的結束。 FutureTask 在java并發編程學習之三種線程啟動方式中有提過。主要的方法如下: cancel(boolean mayInterruptIfRunni...
閱讀 1008·2021-10-27 14:15
閱讀 2763·2021-10-25 09:45
閱讀 1923·2021-09-02 09:45
閱讀 3357·2019-08-30 15:55
閱讀 1798·2019-08-29 16:05
閱讀 3189·2019-08-28 18:13
閱讀 3109·2019-08-26 13:58
閱讀 442·2019-08-26 12:01