Java并发包学习3


Java并发包学习2
Java并发包学习1


5:Future和Callable的使用

在JDK1.5的并发包可以使用Future和Callable来使线程具有返回值的功能。

Callable与Runnable的主要区别:

  • Callable接口的call()方法可以有返回值,而Runnable的run()没有。
  • call()方法可以声明抛出异常,而run()方法不能。

5.1.:Callable的简单使用

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

5.2: get()和sumbit()方法的使用

1、 get()方法:

  • get()方法是具有阻塞特性的,它可以结合future中的isDone()方法来搭配使用,isDone()是判断future中的线程是否完成。
  • get(long timeout,TimeUnit unit): 在指定的时间内等待获得返回值,如果在指定时间内没有获取到则抛出异常。

2、submit()方法:

  • submit不仅可以传入Callable还可以传入Runnable,说明它支持有返回值和无返回值的功能,如果没有返回值的话它输出null。
  • submit(Runnable,T result): 第二个参数result可以作为执行结果的返回值,而不需要使用get()方法来获取,即将result作为默认返回值返回。
  • 与execute()的区别: execute()没有返回值,默认遇到异常直接抛出,但可以通过自定义线程工厂来捕获,而submit()可以使用try/catch来捕获异常。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

5.3:其他API的使用

在这里插入图片描述
在这里插入图片描述

  • cancel(boolean mayInterruptIfRunning): 参数的作用是如果线程正在运行是否中断,在代码中需要使用if(Thread.currentThread().isInterrupted()==true)进行搭配。返回值是发送取消命令是否成功完成(而不是线程是否中断成功),如果线程本身已经结束了,在发送cancel来取消线程,则返回false。
  • isCancelled():线程是否取消。
  • 它可以与之前的线程池那样可以定制线程工厂与自定义拒绝策略。

5.4:Future的缺点

调用get()方法时是阻塞的,一旦前面先行的任务耗时很多,后面的任务调用get()则一直要阻塞,大大影响效率。

6:CompletionService的使用

它的功能是以异步的方式一边生产新的任务,一边处理已完成的任务的结果。使用sumbit提交任务,使用take取得已完成的任务,并按照完成的时间顺序处理结果。
它的实现结构:
在这里插入图片描述

6.1:CompletionService的常用方法:

在这里插入图片描述
常用方法解析:
在这里插入图片描述
在这里插入图片描述
方法Poll(long timeout ,TimeUnit unit)的实验:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

6.2:CompletionService与异常

在这里插入图片描述
第一种情况:不使用get方法,不出现异常
在这里插入图片描述

在这里插入图片描述
第二种:使用get,B出现异常,A顺利执行。
在这里插入图片描述

在这里插入图片描述
第三种如果执行顺序为先执行B,再执行A,则他们的结果都不能输出。
在这里插入图片描述
在这里插入图片描述

7:ExecutorService的使用

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

7.1.1: invokeAny的使用介绍

在这里插入图片描述
补充: 该方法具有阻塞特性,它会等待取得第一个完成任务的值,当第一个任务执行后,会调用interrupt()来将其他任务打断,其他任务可以结合if(Thread.currentThread().isInterrupted()==true)代码来决定线程是否继续执行下去。如果再上面if判断内的代码,使用的是throws new InterruptException来中断线程,虽然异常成功抛出,但是在main线程中是不会捕获它的。要想捕获异常,需在Callable中使用try/catch来进行捕获。

7.1.2:invokeAny与异常

假设A是执行较快的线程,B是执行较慢的线程

1、B出现异常: 主线程成功取得A的返回值,B出现异常,默认情况下不会在控制台打印异常信息,如果使用try/catch捕获异常则可以打印异常信息。
2、A出现异常: 默认不打印异常信息,等待B任务完成。A出现异常不影响B任务的取值,原理是源代码中一直在判断有没有正确的返回值。如果A任务捕获了异常,但没有用throws重新抛出,则主线程不会取得B结果值,因为A没有向主线程上报异常信息,主线程认为A是正确的,所以并不会把关注点放在B上。
3、A,B都出现异常: 最终的异常出现在B上。

总结:使用invokeAny方法的某一个任务正确执行,则其他Callable抛出的异常并不处理。 都没有正确返回值,则处理最后被抛出的异常。

7.1.3:异常情况的验证

B出现异常
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

A出现异常
在这里插入图片描述在这里插入图片描述

A,B都出现异常
在这里插入图片描述
A出现异常捕获了,但未上报,B不出现异常
在这里插入图片描述
在这里插入图片描述
使用catch捕获了异常,有了打印信息,但是没有再次抛出,使得主线程认为A的返回结果是正确的,使用了A的结果。

如果将A中注释的抛出异常代码正常执行得到的结果是:
在这里插入图片描述
在这里插入图片描述

7.2.invokeAll方法的使用

7.2.1:invokeAll方法介绍

它会返回所有任务的执行结果,并且具有阻塞特性,要把所有结果都取回才继续向下执行。它与invokeAny并一样,它可以捕获Callable抛出的异常。

7.2.2:invokeAll与异常

invokeAll异常情况与快慢并无太大的关系,因为它返回的是个Future数组,如果你在得到Future数组之后,并未使用get方法去取任务的返回值,则主线程不会出现异常。而出现了异常则说明你在此时使用了返回结果不正确的Future的get()方法。
例如:线程A出现异常(Callable代码跟上一节一样)
在这里插入图片描述

在这里插入图片描述

7.2.3 invoke方法加入超时参数

invokeAll​(Collection<? extends Callable> tasks, long timeout, TimeUnit unit)作用是如果全部任务没有在指定时间内完成,则出现异常。
在上节代码的基础上仅修改invoke方法:
在这里插入图片描述
在这里插入图片描述
结果没有发生改变,说明即使是错误的返回结果,invoke方法仍认为它是成功返回的。

一个超时,一个不超时打印结果
将A出现异常的代码注释掉
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孤独的偷学者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值