Java 并发编程实战
王宝令
资深架构师
72485 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 51 讲
学习攻略 (1讲)
Java 并发编程实战
15
15
1.0x
00:00/00:00
登录|注册

23 | Future:如何用多线程实现最优的“烧水泡茶”程序?

get(timeout, unit)
get()
isDone()
isCancelled()
cancel()
submit(Runnable task, T result)
submit(Callable task)
submit(Runnable task)
优化串行询价应用
用Future解决任务之间的依赖关系问题
利用多线程可以将串行的任务并行化,提高性能
利用Java并发包提供的Future可以获得异步任务的执行结果
用Future特性来实现等待
用两个线程T1和T2来完成烧水泡茶程序
分工、同步和互斥
实现了Runnable和Future接口
两个构造函数
Future接口的方法
ThreadPoolExecutor提供的3个submit()方法和1个FutureTask工具类来支持获得任务执行结果的需求
课后思考
总结
实现最优的“烧水泡茶”程序
FutureTask工具类
如何获取任务执行结果
Future:如何用多线程实现最优的“烧水泡茶”程序?
参考文章

该思维导图由 AI 生成,仅供参考

在上一篇文章《22 | Executor 与线程池:如何创建正确的线程池?》中,我们详细介绍了如何创建正确的线程池,那创建完线程池,我们该如何使用呢?在上一篇文章中,我们仅仅介绍了 ThreadPoolExecutor 的 void execute(Runnable command) 方法,利用这个方法虽然可以提交任务,但是却没有办法获取任务的执行结果(execute() 方法没有返回值)。而很多场景下,我们又都是需要获取任务的执行结果的。那 ThreadPoolExecutor 是否提供了相关功能呢?必须的,这么重要的功能当然需要提供了。
下面我们就来介绍一下使用 ThreadPoolExecutor 的时候,如何获取任务执行结果。

如何获取任务执行结果

Java 通过 ThreadPoolExecutor 提供的 3 个 submit() 方法和 1 个 FutureTask 工具类来支持获得任务执行结果的需求。下面我们先来介绍这 3 个 submit() 方法,这 3 个方法的方法签名如下。
// 提交Runnable任务
Future<?>
submit(Runnable task);
// 提交Callable任务
<T> Future<T>
submit(Callable<T> task);
// 提交Runnable任务及结果引用
<T> Future<T>
submit(Runnable task, T result);
你会发现它们的返回值都是 Future 接口,Future 接口有 5 个方法,我都列在下面了,它们分别是取消任务的方法 cancel()、判断任务是否已取消的方法 isCancelled()、判断任务是否已结束的方法 isDone()以及2 个获得任务执行结果的 get() 和 get(timeout, unit),其中最后一个 get(timeout, unit) 支持超时机制。通过 Future 接口的这 5 个方法你会发现,我们提交的任务不但能够获取任务执行结果,还可以取消任务。不过需要注意的是:这两个 get() 方法都是阻塞式的,如果被调用的时候,任务还没有执行完,那么调用 get() 方法的线程会阻塞,直到任务执行完才会被唤醒。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了如何利用Java的多线程实现最优的“烧水泡茶”程序。通过介绍ThreadPoolExecutor的submit()方法和FutureTask工具类,详细讲解了三个submit()方法的使用和区别,以及FutureTask的使用方式。通过示例代码展示了如何利用Future特性实现烧水泡茶的最优分工方案,使用两个线程分别完成烧水和泡茶的任务,并通过FutureTask实现任务之间的依赖关系。总结了利用Java并发包提供的Future可以轻松获得异步任务的执行结果,并强调了多线程可以快速将串行任务并行化,提高性能的优势。对于想要了解并发编程的读者具有很高的参考价值。同时,文章还提出了一个优化串行询价应用的问题,引发读者思考并留言分享解决方案。整体而言,本文内容丰富,涵盖了多线程编程的核心概念和实际应用,适合技术人员深入学习和应用。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 并发编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(59)

  • 最新
  • 精选
  • aroll
    建议并发编程课程中的Demo代码,尽量少使用System.out.println, 因为其实现有使用隐式锁,一些情况还会有锁粗化产生

    作者回复: 好建议

    2019-04-20
    14
    106
  • Joker
    ``` java ExecutorService futuresPool = Executors.newFixedThreadPool(3); Future<Price> future1 = futuresPool.submit(this::getPriceByS1); Future<Price> future2 = futuresPool.submit(this::getPriceByS2); Future<Price> future3 = futuresPool.submit(this::getPriceByS3); ExecutorService saveThreadPool = Executors.newFixedThreadPool(3); saveThreadPool.execute(() -> { try { Price r1= future1.get(); save(r1); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }); saveThreadPool.execute(() -> { try { Price r2= future2.get(); save(r2); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }); saveThreadPool.execute(() -> { try { Price r3= future3.get(); save(r3); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }); ``` 用三个线程把这个并行执行,麻烦老师看看,谢谢

    作者回复: 我觉得没问题。也可以用一个线程池,把查询和保存放一个线程里。

    2019-11-06
    5
    19
  • 张天屹
    我不知道是不是理解错老师意思了,先分析依赖有向图,可以看到三条线,没有入度>1的节点 那么启动三个线程即可。 图: s1询价 -> s1保存 s2询价 -> s2保存 s3询价 -> s3保存 代码: new Thread(() -> { r1 = getPriceByS1(); save(r1); }).start(); new Thread(() -> { r2 = getPriceByS2(); save(r2); }).start(); new Thread(() -> { r3 = getPriceByS3(); save(r3); }).start(); 我觉得这里不需要future,除非询价和保存之间还有别的计算工作

    作者回复: 用线程池就用到了

    2019-04-20
    4
    10
  • 魏斌斌
    老师,我看了下futruerask的源码,当调用futrue.get()方法,其实最终会调用unsafe方法是当前线程阻塞。但是我不太理解线程阻塞到哪去了,也没看到锁。

    作者回复: 可以看看操作系统原理有关线程,进程的那部分

    2019-06-17
    3
    8
  • Sunqc
    老师,你所说的订蛋糕,我这样理解对吗,把任务提交给线程池就是让蛋糕店做蛋糕;去看电影就是主线程做其他事,提货单是对应调用future的get

    作者回复: 理解的对

    2019-04-30
    5
  • the only Mia’s
    老师,jdk 8提供的CompletableFuture,以后异步处理是不是可以直接用此替代

    作者回复: 可以

    2020-07-25
    3
  • linqw
    课后习题,老师帮忙看下哦 public class ExecutorExample { private static final ExecutorService executor; static {executor = new ThreadPoolExecutor(4, 8, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1000), runnable -> null, (r, executor) -> {//根据业务降级策略}); } static class S1Task implements Callable<String> { @Override public String call() throws Exception {return getPriceByS1();}} static class S2Task implements Callable<String> { @Overridepublic String call() throws Exception {return getPriceByS2();}} static class S3Task implements Callable<String> {@Override public String call() throws Exception {return getPriceByS3();}} static class SaveTask implements Callable<Boolean> {private List<FutureTask<String>> futureTasks; public SaveTask(List<FutureTask<String>> futureTasks) {this.futureTasks = futureTasks; } @Override public Boolean call() throws Exception { for (FutureTask<String> futureTask : futureTasks) { String data = futureTask.get(10, TimeUnit.SECONDS); saveData(data); } return Boolean.TRUE; } } private static String getPriceByS1() { return "fromDb1"; } private static String getPriceByS2() { return "fromDb2"; } private static String getPriceByS3() { return "fromDb3"; } private static void saveData(String data) { //save data to db } public static void main(String[] args) { S1Task s1Task = new S1Task();FutureTask<String> st1 = new FutureTask<>(s1Task);S2Task s2Task = new S2Task();FutureTask<String> st2 = new FutureTask<>(s2Task);S3Task s3Task = new S3Task();FutureTask<String> st3 = new FutureTask<>(s3Task);List<FutureTask<String>> futureTasks = Lists.<FutureTask<String>>newArrayList(st1, st2, st3);FutureTask<Boolean> saveTask = new FutureTask<>(new SaveTask(futureTasks));executor.submit(st1);executor.submit(st2);executor.submit(st3);executor.submit(saveTask);}}

    作者回复: 没问题,就是有点复杂,代码还可以精简一下

    2019-04-22
    2
    3
  • henry
    现在是在主线程串行完成3个询价的任务,执行第一个任务,其它2个任务只能等待执行,如果要提高效率,这个地方需要改进,可以用老师今天讲的futuretask,三个询价任务改成futuretask并行执行,效率会提高

    作者回复: 👍

    2019-04-20
    3
  • near
    老师,有问题问一下:1.在泡茶的例子中,如果使用线程池创建线程,假设有很多个泡茶任务都要反复调用线程池中的线程,那么在T2提前完成任务,T1获取T2的结果前,T2这个线程会不会被线程池回收?2.假设T1在T2前完成,当T1要获取T2结果时,T1中的代码是阻塞的状态吗?

    作者回复: T2会回收,T1状态是阻塞

    2020-10-13
    1
  • 张德
    我也同意张天屹同学的观点 这个询价操作如果之间没有联系的话 直接起三个线程就可以了 老师能不能讲一下 用线程池怎么就有关联了?

    作者回复: 如果每分钟询价1万次,还能直接创建线程吗?联系指的是线程池和future,不是三个查询操作

    2019-04-21
    1
收起评论
显示
设置
留言
59
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部