并发基础之Callable与Future
创始人
2025-05-31 14:48:16
0

目录

    • 前言
    • 浅析Callale接口
      • Callale源码分析
      • Callale简单演示
    • 浅析Future接口
      • Future源码分析
      • Future简单演示
    • 结语

前言

在多线程编程中,我们一般会根据实际业务场景来创建线程实现功能。当然创建线程的方式也有很多,比如继承Thread类、实现Runable接口或直接根据Executors类创建线程池获取线程。但是继承Thread类和实现Runable并不能获取线程执行结果,如果我们有需求要获取执行结果怎么处理呢?答案就是今天的主角——Callale与Future接口。

浅析Callale接口

Callale源码分析

进入JUC 包下找到 Callable 接口

@FunctionalInterface
public interface Callable {/*** Computes a result, or throws an exception if unable to do so.** @return computed result* @throws Exception if unable to compute a result*/V call() throws Exception;
}

如上源码所示,Callable 接口内部有个call() 方法,该方法可以返回结果,如果有异常情况会抛出异常。call() 方法是 Callable 接口唯一且核心方法,我们在实际开发场景中,在call() 方法中写入具体逻辑代码并可以返回我们需要的结果。

我们继续查看 Callable 接口实现类
在这里插入图片描述

如图所示我们有很多工具类实现了 Callable接口,比如比较常见的 RunableAdapter、TaskCallable等等工具类,在实际业务场景我们可以任意选用我们喜欢的方式实现自己的多线程功能。

Callale简单演示

比如我们现在需要在业务场景中使用多线程执行,并且需要返回执行中的一个结果。那么,我们直接实现Callable即可:

/*** TestCallable* @author senfel* @date 2023/3/20 11:06 * @return void*/
@Test
public void TestCallable() throws Exception{Callable callable = new Callable() {@Overridepublic String call() throws Exception {//具体的业务场景省略 //......//模拟返回执行结果return "测试1";}};//获取执行结果String call = callable.call();//打印结果在控制台System.err.println(call);
}

这里我们直接是简便的编码方式,当前不怕麻烦的同学可以单独写一个类实现Callable接口,然后在实例化自身的线程类并调用。

浅析Future接口

Future源码分析

上文我们讲到了实现Callable接口创建线程并获取执行接口,下面我们看看实现Future 接口方式有何异同。我们先来看源码:

package java.util.concurrent;/**
* Future接口
*/
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 接口内部提供了很多很多的操作方法。比如 cancel() 取消执行、isCancelled () 获取是否取消状态、isDone() 任务是否完成,当然还有我们最重要的 get() 获取执行结果的方法。

看到这里我们不难发现,Future 接口比 Callable接口多了很多很多的操作线程的方法。这些操作方式可以使得线程操作更加灵活,也能够让开发人员适用跟过的业务场景编码。我想说,看到Future 接口我就不想用 Callable了呢。

继续查看Futrue实现类,我们看看有哪些
在这里插入图片描述

如图所示,有很多的实现类。其中有大家不陌生并且经常使用到的ForkJoinTask、FutureTask,这里两个实现类应该算是在多线程开发中经常使用的吧。

我们选一个实现类进入查看Future具体的实现方式吧,我们就用 FutureTask 开探究为啥 Future要比Callable多那么多的操作方法。

进入 JUC FutureTask 查看源码

public interface RunnableFuture extends Runnable, Future {/*** Sets this Future to the result of its computation* unless it has been cancelled.*/void run();
}
public class FutureTask implements RunnableFuture {/** 执行线态* NEW -> COMPLETING -> NORMAL* NEW -> COMPLETING -> EXCEPTIONAL* NEW -> CANCELLED* NEW -> INTERRUPTING -> INTERRUPTED*/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;/** The underlying callable; nulled out after running */private Callable callable;/** The result to return or exception to throw from get() */private Object outcome; // non-volatile, protected by state reads/writes/** The thread running the callable; CASed during run() */private volatile Thread runner;/** Treiber stack of waiting threads */private volatile WaitNode waiters;/*** Returns result or throws exception for completed task.** @param s completed state value*/@SuppressWarnings("unchecked")private V report(int s) throws ExecutionException {Object x = outcome;if (s == NORMAL)return (V)x;if (s >= CANCELLED)throw new CancellationException();throw new ExecutionException((Throwable)x);}/*** Creates a {@code FutureTask} that will, upon running, execute the* given {@code Callable}.** @param  callable the callable task* @throws NullPointerException if the callable is null*/public FutureTask(Callable callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW;       // ensure visibility of callable}/*** Creates a {@code FutureTask} that will, upon running, execute the* given {@code Runnable}, and arrange that {@code get} will return the* given result on successful completion.** @param runnable the runnable task* @param result the result to return on successful completion. If* you don't need a particular result, consider using* constructions of the form:* {@code Future f = new FutureTask(runnable, null)}* @throws NullPointerException if the runnable is null*/public FutureTask(Runnable runnable, V result) {this.callable = Executors.callable(runnable, result);this.state = NEW;       // ensure visibility of callable}public boolean isCancelled() {return state >= CANCELLED;}public boolean isDone() {return state != NEW;}public boolean cancel(boolean mayInterruptIfRunning) {if (!(state == NEW &&UNSAFE.compareAndSwapInt(this, stateOffset, NEW,mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))return false;try {    // in case call to interrupt throws exceptionif (mayInterruptIfRunning) {try {Thread t = runner;if (t != null)t.interrupt();} finally { // final stateUNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);}}} finally {finishCompletion();}return true;}}

如上源码 FutureTask 实现了 RunnableFuture 接口,RunnableFuture 又是实现了Runable和Future,所以FutureTask自然拥有了Future的功能。但是我们也看到FutureTask 构造方法需要传入实例化线程,这里可以是 Callable 实现的线程。分析到这里我们就明白了什么Future 要比 Callable多那么多的方法?原因其实就是Future是对线程操作的升华,不仅仅创建执行线程,更多的是对创建线程执行状态的维护。

Future简单演示

既然上文我们分析了FutureTask ,这里就用FutureTask 简单演示吧

    /*** TestFutureTask* @author senfel* @date 2023/3/20 11:34* @return void*/@Testpublic void TestFutureTask() throws Exception {//实例化FutureTask,并传入一个用Callable实现的线程FutureTask task = new FutureTask(new Callable() {@Overridepublic String call() throws Exception {return "TestFutureTask";}});//运行任务task.run();//获取执行结果String s1 = task.get();//结果打印System.err.println(s1);}
}

这里我们直接也是简便的编码方式,当前不怕麻烦的同学可以单独写一个类实现Callable接口,然后在实例化FutrueTask时传入。

当然,在使用Futrue过程中我们推荐线程池获取Futrue的方式

/*** TestCallableByPool** @author senfel* @date 2023/3/20 11:43 * @return void*/
@Test
public void TestCallableByPool() throws Exception{//创建一个定长线程池ExecutorService executorService = Executors.newFixedThreadPool(10);//获取FutureFuture future = executorService.submit(new Callable() {@Overridepublic String call() throws Exception {//具体的业务场景省略//......//模拟返回执行结果return "TestCallableByPool";}});String s = future.get();System.err.println(s);
}

结语

在多线程编码需要获取线程执行结果的情况下,我们推荐使用实现Futrue 接口的方式,它比Callable方式多了很多的操作执行线程的方法,比如 cancel() 取消执行、isCancelled () 获取是否取消状态、isDone() 任务是否完成等等。在实际的开发中,我们也推荐使用多线程来创建Futrue获取执行结果。

相关内容

热门资讯

去年前三季净利下滑近两成,奕帆... 深圳商报·读创客户端记者 张弛 1月21日,奕帆传动(301023.SZ)开盘即封住20%涨停,截至...
AI应用ETF的“故事”讲完了... 2026年开年,AI应用板块无疑是市场最瞩目的焦点之一,AI投资风向从算力基础设施向具体应用场景扩散...
银行应该承担房价下跌的损失吗? 最近看到一个观点,说是银行应该承担房价下跌的损失,因为大洋对岸的漂亮国就是这么做的, 在M国你可以主...
新股消息 | 先导智能(300... 来源:智通财经网 1月21日,中国证监会国际合作司发布《关于无锡先导智能装备股份有限公司境外发行上市...
区工商联赴蓉开展“翠商兴翠 同... 为深化跨区域合作交流,凝聚在外翠商力量,精准服务民营经济高质量发展,1月16日,区委统战部副部长、区...
金价银价又飙升!自2025年初... 在地缘局势紧张加剧、市场风险规避情绪高涨背景下,现货黄金以及黄金和白银期货价格20日大幅上涨,再创历...
【社论】上调中国经济增长预期何... 近日,国际货币基金组织(IMF)在最新发布的《世界经济展望》中,将2026年中国经济增长率上调0.3...
五张图带你入门ETF,这个对投... 来源:市值风云 想投资ETF?从零到一的基础知识都在这儿了。 作者 | 市值风云基金研究部 编辑 ...
年赚9亿,这家深圳大卖赴港上市... 近日,深圳大卖道通科技交出了一份惊人的成绩单:预计2025年归母净利润高达9亿至9.3亿元,同比猛增...
冯冀生卖股还债后退出,钱大妈I... 文/瑞财经 杨宏彬 20点以后上哪买菜?钱大妈或许是广东人民的第一选择。 钱大妈严格践行“不卖隔夜肉...
贝森特称日本国债抛售潮外溢至美... 来源:滚动播报 日本政府债券遭到抛售之际,美国财政部长斯科特·贝森特表示,他与日本对口官员进行了交谈...
投资铜条火了,一公斤约200元 图片来源:社交网络平台 1月19日,现货黄金突破4690美元/盎司,现货白银突破94美元/盎司,双双...
美股回调A股反常回暖,假的!主... 其实隔夜美股回调,加上近期降温调控,大家对市场已经不太感冒,结果今天却是低开高走自发回暖,行情又好起...
【政策速递】养老、托育、家政等... 来源:国家税务总局微信、新华社 财政部、税务总局等六部门近日发布《关于延续实施养老、托育、家政等社区...
泡泡玛特,发布首款潮玩手机 本文自南都·湾财社 采写 | 南都·湾财社记者 严兆鑫 编辑 | 子衿 发布会次日,即1月20日,泡...
枪打出头鸟!特朗普通告全球:加... 突发消息传来,特朗普发布全球通告,宣布对法国的葡萄酒和香槟征收高达200%的关税。法国,这个曾在欧美...
成都企业,掀起IPO热潮 每经记者|谢陶 每经编辑|唐元 近日来,成都企业在资本市场动作频频,引发各界高度关注。 《每日经济...
周大福珠宝:珠宝消费气氛逐步改... 1月21日下午,港股上市公司周大福珠宝发布了截至2025年12月31日止三个月(“第三财季”)未经审...
和讯投顾陆润凯:尾盘ETF放量... 看来这一波市场的压盘还有高手,尾盘很多宽基ETF继续放量,上证50ETF成交超150亿,创历史天量硬...
南京2026年首场宅地土拍落槌... 观点网讯:1月21日 ,南京迎来2026年首场宅地土拍,位于江北核心区南工大板块的地块由南京新高市政...