电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術(shù)文章
文章詳情頁

解析Java異步之call future

瀏覽:21日期:2022-08-10 15:14:20
目錄一、概述二、Callable、Executor 與 Future2.1、任務(wù):Callable2.2、執(zhí)行:ExecutorService2.3、結(jié)果:Future三、使用舉例3.1、使用Future3.2、使用FutureTask四、總結(jié)一、概述

我們大家都知道,在 Java 中創(chuàng)建線程主要有三種方式:

繼承 Thread 類; 實(shí)現(xiàn) Runnable 接口; 實(shí)現(xiàn) Callable 接口。

而后兩者的區(qū)別在于 Callable 接口中的 call() 方法可以異步地返回一個(gè)計(jì)算結(jié)果 Future,并且一般需要配合ExecutorService 來執(zhí)行。這一套操作在代碼實(shí)現(xiàn)上似乎也并不難,可是對(duì)于call()方法具體怎么(被ExecutorService)執(zhí)行的,以及 Future 這個(gè)結(jié)果是怎么獲取的,卻又不是很清楚了。

那么本篇文章,我們就一起來學(xué)習(xí)下 Callable 接口以及 Future 的使用,主要面向兩個(gè)問題:

承載著具體任務(wù)的 call() 方法如何被執(zhí)行的? 任務(wù)的執(zhí)行結(jié)果如何得到?

你可能會(huì)說,這兩個(gè)難道不是一個(gè)問題嗎?任務(wù)執(zhí)行了就會(huì)有返回結(jié)果,而返回結(jié)果也一定是任務(wù)執(zhí)行了才返回的,難道還能返回一個(gè)其他任務(wù)的結(jié)果么??不要著急,耐心的看下去,你就會(huì)發(fā)現(xiàn),這兩個(gè)還真的就是一個(gè)問題。

本文將分為兩個(gè)部分,第一部分分別介紹 任務(wù)、執(zhí)行、以及結(jié)果這三個(gè)概念在 Java API 中的實(shí)體和各自的繼承關(guān)系,第二部分通過一個(gè)簡(jiǎn)單的例子回顧他們的用法,再理解下這兩個(gè)問題的答案。

二、Callable、Executor 與 Future

既然是一個(gè)任務(wù)被執(zhí)行并返回結(jié)果,那么我們先來看看具體的任務(wù),也就是 Callable 接口。

2.1、任務(wù):Callable

非常簡(jiǎn)單,只包含一個(gè)有泛型返回值的 call() 方法,需要在最后返回定義類型的結(jié)果。如果任務(wù)沒有需要返回的結(jié)果,那么將泛型 V 設(shè)為 void 并return null;就可以了。對(duì)比的是 Runnable,另一個(gè)明顯的區(qū)別則是 Callable可以拋出異常。

public interface Callable<V> { V call() throws Exception;}public interface Runnable { public abstract void run();}2.2、執(zhí)行:ExecutorService

說到線程就少不了線程池,而說到線程池肯定離不開 Executor 接口。下面這幅圖是 Executor 的框架,我們常用的是其中的兩個(gè)具體實(shí)現(xiàn)類 ThreadPoolExecutor 以及 ScheduledThreadPoolExecutor,在 Executors 類中通過靜態(tài)方法獲取。Executors 中包含了線程池以及線程工廠的構(gòu)造,與 Executor 接口的關(guān)系類似于 Collection 接口和 Collections 類的關(guān)系。

解析Java異步之call future

那么我們自頂向下,從源碼上了解一下 Executor 框架,學(xué)習(xí)學(xué)習(xí)任務(wù)是如何被執(zhí)行的。首先是 Executor 接口,其中只定義了 execute() 方法。

public interface Executor { void execute(Runnable command);}

ExecutorService 接口繼承了 Executor 接口,主要擴(kuò)展了一系列的 submit() 方法以及對(duì) executor 的終止和判斷狀態(tài)。以第一個(gè)<T> Future<T> submit(Callable<T> task);為例,其中 task 為用戶定義的執(zhí)行的異步任務(wù),F(xiàn)uture 表示了任務(wù)的執(zhí)行結(jié)果,泛型 T 代表任務(wù)結(jié)果的類型。

public interface ExecutorService extends Executor { void shutdown();// 現(xiàn)有任務(wù)完成后停止線程池 List<Runnable> shutdownNow(); // 立即停止線程池 boolean isShutdown(); // 判斷是否已停止 boolean isTerminated(); <T> Future<T> submit(Callable<T> task);// 提交Callale任務(wù) <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task); // 針對(duì)Callable集合的invokeAll()等方法}

抽象類AbstractExecutorService 是 ThreadPoolExecutor 的基類,在下面的代碼中,它實(shí)現(xiàn)了ExecutorService 接口中的 submit() 方法。注釋中是對(duì)應(yīng)的 newTaskFor() 方法的代碼,非常簡(jiǎn)單,就是將傳入的Callable 或 Runnable 參數(shù)封裝成一個(gè) FutureTask 對(duì)象。

// 1.第一個(gè)重載方法,參數(shù)為Callablepublic <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); // return new FutureTask<T>(callable); execute(ftask); return ftask;}// 2.第二個(gè)重載方法,參數(shù)為Runnablepublic Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); // return new FutureTask<T>(task, null); execute(ftask); return ftask;}// 3.第三個(gè)重載方法,參數(shù)為Runnable + 返回對(duì)象public <T> Future<T> submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task, result); // return new FutureTask<T>(task, result); execute(ftask); return ftask;}

那么也就是說,無論傳入的是 Callable 還是 Runnable,submit() 方法其實(shí)就做了三件事

解析Java異步之call future

具體來說,submit() 中首先生成了一個(gè) RunnableFuture 引用的 FutureTask 實(shí)例,然后調(diào)用 execute() 方法來執(zhí)行它,那么我們可以推測(cè) FutureTask 繼承自 RunnableFuture,而 RunnableFuture 又實(shí)現(xiàn)了 Runnable,因?yàn)閑xecute() 的參數(shù)應(yīng)為 Runnable 類型。上面還涉及到了 FutureTask 的構(gòu)造函數(shù),也來看一下。

public FutureTask(Callable<V> callable) { this.callable = callable; this.state = NEW;}public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); // 通過適配器將runnable在call()中執(zhí)行并返回result this.state = NEW;}

FutureTask 共有兩個(gè)構(gòu)造方法。第一個(gè)構(gòu)造方法比較簡(jiǎn)單,對(duì)應(yīng)上面的第一個(gè) submit(),采用組合的方式封裝Callable 并將狀態(tài)設(shè)為NEW;而第二個(gè)構(gòu)造方法對(duì)應(yīng)上面的后兩個(gè) submit() 重載,不同之處是首先使用了Executors.callable來將 Runnable 和 result 組合成 Callable,這里采用了適配器RunnableAdapter implements Callable,巧妙地在 call() 中執(zhí)行 Runnable 并返回結(jié)果。

static final class RunnableAdapter<T> implements Callable<T> { final Runnable task; final T result;// 返回的結(jié)果;顯然:需要在run()中賦值 RunnableAdapter(Runnable task, T result) { this.task = task; this.result = result; } public T call() { task.run(); return result; }}

在適配器設(shè)計(jì)模式中,通常包含**目標(biāo)接口 Target、適配器 Adapter 和被適配者 Adaptee **三類角色,其中目標(biāo)接口代表客戶端(當(dāng)前業(yè)務(wù)系統(tǒng))所需要的功能,通常為借口或抽象類;被適配者為現(xiàn)存的不能滿足使用需求的類;適配器是一個(gè)轉(zhuǎn)換器,也稱 wrapper,用于給被適配者添加目標(biāo)功能,使得客戶端可以按照目標(biāo)接口的格式正確訪問。對(duì)于 RunnableAdapter 來說,Callable 是其目標(biāo)接口,而 Runnable 則是被適配者。RunnableAdapter 通過覆蓋 call() 方法使其可按照 Callable 的要求來使用,同時(shí)其構(gòu)造方法中接收被適配者和目標(biāo)對(duì)象,滿足了 call() 方法有返回值的要求。

解析Java異步之call future

那么總結(jié)一下 submit() 方法執(zhí)行的流程,就是:Callable 被封裝在 Runnable 的子類中傳入 execute() 得以執(zhí)行。

2.3、結(jié)果:Future

要說 Future 就是異步任務(wù)的執(zhí)行結(jié)果其實(shí)并不準(zhǔn)確,因?yàn)樗砹艘粋€(gè)任務(wù)的執(zhí)行過程,有狀態(tài)、可以被取消,而 get() 方法的返回值才是任務(wù)的結(jié)果。

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;}

我們?cè)谏厦嬷羞€提到了 RuunableFuture 和 FutureTask。從官方的注釋來看,RuunableFuture 就是一個(gè)可以 run的 future,實(shí)現(xiàn)了 Runnable 和 Future 兩個(gè)接口,在 run() 方法中執(zhí)行完計(jì)算時(shí)應(yīng)該將結(jié)果保存起來以便通過 get()獲取。

public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation unless it has been cancelled. */ void run();}

FutureTask 直接實(shí)現(xiàn)了 RunnableFuture 接口,作為執(zhí)行過程,共有下面這幾種狀態(tài),其中 COMPLETING 為一個(gè)暫時(shí)狀態(tài),表示正在設(shè)置結(jié)果或異常,對(duì)應(yīng)的,設(shè)置完成后狀態(tài)變?yōu)?NORMAL 或 EXCEPTIONAL;CANCELLED、INTERRUPTED 表示任務(wù)被取消或中斷。在上面的構(gòu)造方法中,將 state 初始化為 NEW。

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 的主要內(nèi)容,主要是 run() 和 get()。注意 outcome 的注釋,無論是否發(fā)生異常返回的都是這個(gè) outcome,因?yàn)樵趫?zhí)行中如果執(zhí)行成功就將結(jié)果設(shè)置給了它(set()),而發(fā)生異常時(shí)將異常賦給了他(setException()),而在獲取結(jié)果時(shí)也都返回了 outcome(通過report())。

public class FutureTask<V> implements RunnableFuture<V> {private Callable<V> callable; // target,待執(zhí)行的任務(wù)/** 保存執(zhí)行結(jié)果或異常,在get()方法中返回/拋出 */ private Object outcome; // 非volatile,通過CAS保證線程安全 public void run() {......Callable<V> c = callable;if (c != null && state == NEW) { V result; boolean ran; try {result = c.call(); // 調(diào)用call()執(zhí)行用戶任務(wù)并獲取結(jié)果ran = true; // 執(zhí)行完成,ran置為true } catch (Throwable ex) { // 調(diào)用call()出現(xiàn)異常,而run()方法繼續(xù)執(zhí)行 result = null; ran = false; setException(ex); // setException(Throwable t): compareAndSwapInt(NEW, COMPLETING); outcome = t; } if (ran)set(result); // set(V v): compareAndSwapInt(NEW, COMPLETING); outcome = v;} } public V get() throws InterruptedException, ExecutionException {int s = state;if (s <= COMPLETING) s = awaitDone(false, 0L); // 加入隊(duì)列等待COMPLETING完成,可響應(yīng)超時(shí)、中斷return report(s); } public V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException {// 超時(shí)等待 }private V report(int s) throws ExecutionException {Object x = outcome;if (s == NORMAL) // 將outcome作為執(zhí)行結(jié)果返回 return (V)x;if (s >= CANCELLED) throw new CancellationException();throw new ExecutionException((Throwable)x); // 將outcome作為捕獲的返回 }}

FutureTask 實(shí)現(xiàn)了 RunnableFuture 接口,所以有兩方面的作用。

第一,作為 Runnable 傳入 execute() 方法來執(zhí)行,同時(shí)封裝 Callable 對(duì)象并在 run() 中調(diào)用其 call() 方法; 第二,作為 Future 管理任務(wù)的執(zhí)行狀態(tài),將 call() 的返回值保存在 outcome 中以通過 get() 獲取。這似乎就能回答開頭的兩個(gè)問題,并且渾然天成,就好像是一個(gè)問題,除非發(fā)生異常的時(shí)候返回的不是任務(wù)的結(jié)果而是異常對(duì)象。

總結(jié)一下繼承關(guān)系:

解析Java異步之call future

三、使用舉例

文章的標(biāo)題有點(diǎn)唬人,說到底還是講 Callable 的用法。現(xiàn)在我們知道了 Future 代表了任務(wù)執(zhí)行的過程和結(jié)果,作為 call() 方法的返回值來獲取執(zhí)行結(jié)果;而 FutureTask 是一個(gè) Runnable 的 Future,既是任務(wù)執(zhí)行的過程和結(jié)果,又是 call 方法最終執(zhí)行的載體。下面通過一個(gè)例子看看他們?cè)谑褂蒙系膮^(qū)別。

首先創(chuàng)建一個(gè)任務(wù),即定義一個(gè)任務(wù)類實(shí)現(xiàn) Callable 接口,在 call() 方法里添加我們的操作,這里用耗時(shí)三秒然后返回 100 模擬計(jì)算過程。

class MyTask implements Callable<Integer> { @Override public Integer call() throws Exception {System.out.println('子線程開始計(jì)算...');for (int i=0;i<3;++i){ Thread.sleep(1000); System.out.println('子線程計(jì)算中,用時(shí) '+(i+1)+' 秒');}System.out.println('子線程計(jì)算完成,返回:100');return 100; }}

然后呢,創(chuàng)建一個(gè)線程池,并實(shí)例化一個(gè) MyTask 備用。

ExecutorService executor = Executors.newCachedThreadPool();MyTask task = new MyTask();

現(xiàn)在,分別使用 Future 和 FutureTask 來獲取執(zhí)行結(jié)果,看看他們有什么區(qū)別。

3.1、使用Future

Future 一般作為 submit() 的返回值使用,并在主線程中以阻塞的方式獲取異步任務(wù)的執(zhí)行結(jié)果。

System.out.println('主線程啟動(dòng)線程池');Future<Integer> future = executor.submit(task);System.out.println('主線程得到返回結(jié)果:'+future.get());executor.shutdown();

看看輸出結(jié)果:

主線程啟動(dòng)線程池

子線程開始計(jì)算...

子線程計(jì)算中,用時(shí) 1 秒

子線程計(jì)算中,用時(shí) 2 秒

子線程計(jì)算中,用時(shí) 3 秒

子線程計(jì)算完成,返回:100

主線程得到返回結(jié)果:100

由于 get() 方法阻塞獲取結(jié)果,所以輸出順序?yàn)樽泳€程計(jì)算完成后主線程輸出結(jié)果。

3.2、使用FutureTask

由于 FutureTask 集任務(wù)與結(jié)果于一身,所以我們可以使用 FutureTask 自身而非返回值來管理任務(wù),這需要首先利用 Callable 對(duì)象來構(gòu)造 FutureTask,并調(diào)用不同的submit()重載方法。

System.out.println('主線程啟動(dòng)線程池');FutureTask<Integer> futureTask = new FutureTask<>(task);executor.submit(futureTask); // 作為Ruunable傳入submit()中System.out.println('主線程得到返回結(jié)果:'+futureTask.get()); // 作為Future獲取結(jié)果executor.shutdown();

這段程序的輸出與上面中完全相同,其實(shí)兩者在實(shí)際執(zhí)行中的區(qū)別也不大,雖然前者調(diào)用了submit(Callable<T> task)而后者調(diào)用了submit(Runnable task),但最終都通過execute(futuretask)來把任務(wù)加入線程池中。

四、總結(jié)

上面大費(fèi)周章其實(shí)只是盡可能細(xì)致地講清楚了 Callable 中的任務(wù)是如何執(zhí)行的,總結(jié)起來就是:

線程池中,submit() 方法實(shí)際上將 Callable 封裝在 FutureTask 中,將其作為 Runnable 的子類傳給 execute()真正執(zhí)行;FutureTask 在 run() 中調(diào)用 Callable 對(duì)象的 call() 方法并接收返回值或捕獲異常保存在Object outcome中,同時(shí)管理執(zhí)行過程中的狀態(tài)state;FutureTask 同時(shí)作為 Future 的子類,通過 get() 返回任務(wù)的執(zhí)行結(jié)果,若未執(zhí)行完成則通過等待隊(duì)列進(jìn)行阻塞等待完成;

FutureTask 作為一個(gè) Runnable 的 Future,其中最重要的兩個(gè)方法如下。

解析Java異步之call future

以上就是解析Java異步之call future的詳細(xì)內(nèi)容,更多關(guān)于Java異步 call future的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 刺绳_刀片刺网_刺丝滚笼_不锈钢刺绳生产厂家_安平县浩荣金属丝网制品有限公司-安平县浩荣金属丝网制品有限公司 | 国际金融网_每日财经新资讯网 | KBX-220倾斜开关|KBW-220P/L跑偏开关|拉绳开关|DHJY-I隔爆打滑开关|溜槽堵塞开关|欠速开关|声光报警器-山东卓信有限公司 | 光伏家 - 太阳能光伏发电_分布式光伏发电_太阳能光伏网 | 低浓度恒温恒湿称量系统,强光光照培养箱-上海三腾仪器有限公司 | 水冷式工业冷水机组_风冷式工业冷水机_水冷螺杆冷冻机组-深圳市普威机械设备有限公司 | 中医中药治疗血小板减少-石家庄血液病肿瘤门诊部 | 活性氧化铝球|氧化铝干燥剂|分子筛干燥剂|氢氧化铝粉-淄博同心材料有限公司 | 金属管浮子流量计_金属转子流量计厂家-淮安润中仪表科技有限公司 | 今日扫码_溯源二维码_产品防伪一物一码_红包墙营销方案 | OLChemim试剂-ABsciex耗材-广州市自力色谱科仪有限公司 | HEYL硬度计量泵-荧光法在线溶解氧仪-净时测控技术(上海)有限公司 | wika威卡压力表-wika压力变送器-德国wika代理-威卡总代-北京博朗宁科技 | 拉伸膜,PE缠绕膜,打包带,封箱胶带,包装膜厂家-东莞宏展包装 | KBX-220倾斜开关|KBW-220P/L跑偏开关|拉绳开关|DHJY-I隔爆打滑开关|溜槽堵塞开关|欠速开关|声光报警器-山东卓信有限公司 | 医学动画公司-制作3d医学动画视频-医疗医学演示动画制作-医学三维动画制作公司 | 睿婕轻钢别墅_钢结构别墅_厂家设计施工报价 | 滑板场地施工_极限运动场地设计_滑板公园建造_盐城天人极限运动场地建设有限公司 | 北京办公室装修,办公室设计,写字楼装修-北京金视觉装饰工程公司 北京成考网-北京成人高考网 | 吸污车_吸粪车_抽粪车_电动三轮吸粪车_真空吸污车_高压清洗吸污车-远大汽车制造有限公司 | 针焰试验仪,灼热丝试验仪,漏电起痕试验仪,水平垂直燃烧试验仪 - 苏州亚诺天下仪器有限公司 | 常州减速机_减速机厂家_常州市减速机厂有限公司 | 一航网络-软件测评官网 | 北京网站建设|北京网站开发|北京网站设计|高端做网站公司 | 不锈钢复合板厂家_钛钢复合板批发_铜铝复合板供应-威海泓方金属复合材料股份有限公司 | 杭州可当科技有限公司—流量卡_随身WiFi_AI摄像头一站式解决方案 | 氢氧化钙设备_厂家-淄博工贸有限公司 | 液晶拼接屏厂家_拼接屏品牌_拼接屏价格_监控大屏—北京维康 | 澳门精准正版免费大全,2025新澳门全年免费,新澳天天开奖免费资料大全最新,新澳2025今晚开奖资料,新澳马今天最快最新图库 | 油冷式_微型_TDY电动滚筒_外装_外置式电动滚筒厂家-淄博秉泓机械有限公司 | 变压器配件,变压器吸湿器,武强县吉口变压器配件有限公司 | 工业胀紧套_万向节联轴器_链条-规格齐全-型号选购-非标订做-厂家批发价格-上海乙谛精密机械有限公司 | 涂层测厚仪_漆膜仪_光学透过率仪_十大创新厂家-果欧电子科技公司 | 高压包-点火器-高压发生器-点火变压器-江苏天网 | 派克防爆伺服电机品牌|国产防爆伺服电机|高低温伺服电机|杭州摩森机电科技有限公司 | ★店家乐|服装销售管理软件|服装店收银系统|内衣店鞋店进销存软件|连锁店管理软件|收银软件手机版|会员管理系统-手机版,云版,App | 首页_欧瑞传动官方网站--主营变频器、伺服系统、新能源、软起动器、PLC、HMI | 天津力值检测-天津管道检测-天津天诚工程检测技术有限公司 | 旋转/数显粘度计-运动粘度测定仪-上海平轩科学仪器 | 蜂蜜瓶-玻璃瓶-玻璃瓶厂-玻璃瓶生产厂家-徐州贵邦玻璃制品有限公司 | 加中寰球移民官网-美国移民公司,移民机构,移民中介,移民咨询,投资移民 |