Future、FutureTask、Runnable、Callable的关系
发布日期:2021-09-12 09:57:55 浏览次数:46 分类:技术文章

本文共 5221 字,大约阅读时间需要 17 分钟。

以及并在此基础上加了一些其他知识点。

一、Runnable

1、实现该接口并重写run方法2、利用该类的对象创建线程3、线程启动时就会自动调用该对象的run方法

通常在开发中结合ExecutorService使用,将任务的提交与任务的执行解耦开,同时也能更好地利用Executor提供的各种特性

缺点:

1.Runnable接口不能抛出异常,只能内部消化;2.Runnable接口没有返回值

注意:

因为Java单继承多实现,所以相对于集成Thread类,实现Runnable接口要更好。

ExecutorService executor = Executors.newCachedThreadPool();executor.submit(new Runnable() {
public void run() {
//TODO } });executor.shutdown();

ExecutorService继承(扩展)了Executor接口,添加了一些用于生命周期管理的方法。

public interface ExecutorService extends Executor{
void shutdown();//平稳的关闭,不再接收新的任务、同时等待已经提交的任务执行 List
shutdownNow();//执行暴力的关闭,尝试取消所有运行的任务,并且不再启动队列中尚未启动的任务 boolean isShutDown();//如果此执行程序已关闭,则返回 true boolean isTerminated();//查询ExecutorService是否已经终止 boolean awaitTermination(long timeout, TimeUint unit) throws InterruptedException; //通常调用awaitTermination后会立即调用shutdown,产生同步关闭ExecutorServicce Future
submit(Runnable task); //提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功 完成时将会返回 null。 抛出RejectedExecutionException - 如果任务无法安排执行 ,NullPointerException - 如果该任务为 null .....}

二、Callable接口

与Runnable不同的是,Callable是个泛型参数化接口,并能返回线程的执行结果,且能在无法正常计算时抛出异常

public interface Callable
{
V call() throws Exception;}

Callable并不像Runnable那样通过Thread的start方法就能启动实现类的run方法,所以它通常利用ExecutorService的submit方法去启动call方法自执行任务,而ExecutorService的submit又返回一个Future类型的结果,因此Callable通常也与Future一起使用

ExecutorService pool = Executors.newCachedThreadPool();Future
future = pool.submit(new Callable{
public void call(){
//TODO }});

Runnable与Callable不同点:

  1. Runnable不返回任务执行结果,Callable可返回任务执行结果
  2. Callable在任务无法计算结果时抛出异常,而Runnable不能
  3. Runnable任务可直接由Thread的start方法或ExecutorService的submit方法去执行

三、Future

Future保存异步计算的结果,可以在我们执行任务时去做其他工作,并提供了以下几个方法

  • cancel(boolean mayInterruptIfRunning):试图取消执行的任务,参数为true时直接中断正在执行的任务,否则直到当前任务执行完成,成功取消后返回true,否则返回false
  • isCancel():判断任务是否在正常执行完前被取消的,如果是则返回true
  • isDone():判断任务是否已完成
  • get():等待计算结果的返回,如果计算被取消了则抛出
  • get(long timeout,TimeUtil unit):设定计算结果的返回时间,如果在规定时间内没有返回计算结果则抛出TimeOutException

使用Future的好处:

  1. 获取任务的结果,判断任务是否完成,中断任务
  2. Future的get方法很好的替代的了Thread.join或Thread,join(long millis)
  3. Future的get方法可以判断程序代码(任务)的执行是否超时,如:
try{
future.get(60,TimeUtil.SECOND); }catch(TimeoutException timeout){
log4j.log("异常,将被取消!!"); future.cancel(); }

四、FutureTask

先看FutureTask的实现

public class FutureTask
implements RunnableFuture

FutureTask实现了RunnableFuture接口,我们再来看下RunnableFuture接口:

public interface RunnableFuture
extends Runnable, Future
{
void run();}

可以看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

FutureTask提供了2个构造器:

public FutureTask(Callable
callable) {
}public FutureTask(Runnable runnable, V result) {
}

代码例子

1、Callable+Future获取执行结果

public class Test {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool(); Task task = new Task(); Future
result = executor.submit(task); executor.shutdown(); try {
Thread.sleep(1000); } catch (InterruptedException e1) {
e1.printStackTrace(); } System.out.println("主线程在执行任务"); try {
System.out.println("task运行结果"+result.get()); } catch (InterruptedException e) {
e.printStackTrace(); } catch (ExecutionException e) {
e.printStackTrace(); } System.out.println("所有任务执行完毕"); }}class Task implements Callable
{
@Override public Integer call() throws Exception {
System.out.println("子线程在进行计算"); Thread.sleep(3000); int sum = 0; for(int i=0;i<100;i++) sum += i; return sum; }}

2、Callable+FutureTask获取执行结果

public class Test {
public static void main(String[] args) {
//第一种方式 ExecutorService executor = Executors.newCachedThreadPool(); Task task = new Task(); FutureTask
futureTask = new FutureTask
(task); executor.submit(futureTask); executor.shutdown(); //第二种方式,注意这种方式和第一种方式效果是类似的,只不过一个使用的是ExecutorService,一个使用的是Thread /*Task task = new Task(); FutureTask
futureTask = new FutureTask
(task); Thread thread = new Thread(futureTask); thread.start();*/ try {
Thread.sleep(1000); } catch (InterruptedException e1) {
e1.printStackTrace(); } System.out.println("主线程在执行任务"); try {
System.out.println("task运行结果"+futureTask.get()); } catch (InterruptedException e) {
e.printStackTrace(); } catch (ExecutionException e) {
e.printStackTrace(); } System.out.println("所有任务执行完毕"); }}class Task implements Callable
{
@Override public Integer call() throws Exception {
System.out.println("子线程在进行计算"); Thread.sleep(3000); int sum = 0; for(int i=0;i<100;i++) sum += i; return sum; }}

转载地址:https://blog.csdn.net/weixin_38367817/article/details/103794071 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:六、取消与关闭
下一篇:五、结构化并发应用程序之任务执行(Executor)

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年01月30日 05时43分53秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章