JUC----Callable,Future 和 FutureTask三者之间的关系

本文深入解析了Java中Callable接口与Future接口的使用,对比了Runnable与Callable的主要区别,详细介绍了Future的功能及其唯一实现类FutureTask的用法,并通过具体实例展示了如何通过FutureTask获取Callable任务的执行结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

我们无论使用Thread还是Runnable新建线程,它都无法返回结果值。而自从Jdk1.5开始,官方提供了 Callable和Future, 通过他们就可以获得任务的执行结果。其中FutureTask则作为Future的实现类。
在这里插入图片描述

1. Callable

谈到Callable,我们就得顺便提一下它与Runnable接口的区别。
先说一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法:(属于函数式接口)

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

它的run() 方法没有返回值,且不能抛出异常。
而Callable则位于java.util.concurrent 包下,它也是一个函数式接口,声明了call() , 不过该方法可以有返回值还可以抛出异常。

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

在使用上,我们可以参照上面的继承图。通过FutureTask对Callable进行封装,最终通过Future获取结果(下面的实例会进一步说明)
(补充说明:Callable其实也是Executor框架的一部分,详情可参考:JUC----线程池(二)里的第三点)

2. Future

Future就是对具体的Runnable或者Callable任务的执行结果进行取消,查询是否完成,获取结果。比较时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

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

在Future接口下提供了五个方法

  • cancel( ): 该方法用来取消任务,如果取消任务成功返回true, 如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务。

任务还没有开始,mayInterruptIfRunning无论设置为true还是false, 它的返回值都为false (因为任务还没执行)
任务已结束,mayInterruptIfRunning无论设置为true还是false, 它的返回值都为false (因为任务已完成)
任务已经启动,mayInterruptIfRunning设置为true,表示中断正在运行的任务,中断成功则返回true; 若mayInterruptIfRunning设置为false表示不会去中断已经在运行的任务,返回false.

  • isCancelled( ): 表示任务是否被取消成功 ,取消成功返回true
  • isDone(): 表示任务是否已经完成,若完成则返回true.
  • get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
  • get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

从上面的方法可以知道Future提供了三种功能:
1) 判断任务是否完成
2) 能够中断任务
3) 能够获取执行结果
但由于Future只是一个接口,为了使用它里面的功能,我们必须使用它的间接继承类 FutureTask。

3. FutureTask

它实现了了RunnableFuture接口,而RunnableFuture接口又继承自Runnable和Future接口,所以FutureTask既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
在这里插入图片描述
在这里插入图片描述
相关构造器:

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

事实上,FutureTask是Future接口的一个唯一实现类。

**使用场景上:**当前的计算任务相对比较耗时,但在未来的某一刻,还是需要用到该计算结果的时候,我们可以使用Future, FutureTask。
例如:对于一个文档里面包含有多种类型的文件时,图片(云端)获取时间相对较长;文字(本地)获取时间相对较短;我们这里可以开启多个线程去拿资源,然后在最终资源收集上使用FutureTask.get() 去阻塞获取我们图片的资源。

4. 使用实例

我们这里开启一个线程去执行计算任务,然后在主线程通过一个FutureTask.get() 以阻塞的方式获取计算结果。(这里可以通过随机值来表示获取结果或者取消任务)

package 并发工具类.future;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/*Future的测试*/
public class UseFuture {

    /*实现Callable接口,允许有返回值*/
    private static class UseCallable implements Callable<Integer>{

        private int sum;
        @Override
        public Integer call() throws Exception {
            System.out.println("Callable子线程开始计算");

            Thread.sleep(2000);
            for (int i = 0; i < 5000; i++) {
                sum = sum + i;
            }
            System.out.println("Callable子线程计算完成,结果="+sum);
            return sum;
        }
    }

    public static void main(String[] args) throws Exception {
        UseCallable useCallable = new UseCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(useCallable);
        new Thread(futureTask).start();

        Random r = new Random();
        Thread.sleep(1000);
        if (r.nextBoolean()){ //随机决定是获得结果还是停止任务
            System.out.println("Get UseCallable result = "+ futureTask.get());
        }else{
            System.out.println("中断计算");
            futureTask.cancel(true);
        }
    }

}

当r.nextBoolean() 取得true时,表示获取结果值
在这里插入图片描述
当r.nextBoolean() 取得false时,表示取消任务
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值