JAVA多线程之——Future与Callable

Java多线程中,除了继承Thread或实现Runnable,还可以使用Callable和Future获取线程执行结果。Callable的call方法返回泛型对象,而Future则用于获取或检查Callable任务的结果。在ExecutorService中,submit方法用于提交Callable任务,返回一个Future对象。FutureTask作为RunnableFuture的实现,同时实现了Runnable和Future接口,封装Callable并在run方法中调用call方法。

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

Futrue与Callable

在学习了多线程之后,可以知道线程的实现方式是通过继承Thread或者实现Runnable接口,当线程被启动后,会执行run方法。都知道run方法是一个void方法,也就是说线程执行完毕之后,不会返回任何东西。那么,如果我们想得到线程执行的结果怎么呢?这就要用到JUC中的Futrue与Callable了。
Callable

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

Callable方法只有一个call方法,该方法返回一个泛型V对象。对比Runnable接口,该接口只有run方法,无返回值。
在调用run方法的时候,我们通常通过new一个Thread方法,传入一个Runnable接口。同理,Callable一般情况下是配合ExecutorService来使用的,在ExecutorService接口中声明了若干个submit方法的重载版本:

 <T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);

常用的是第一个和第三个。先来看一下第一个:

public <T> Future<T> submit(Callable<T> task) {
       if (task == null) throw new NullPointerException();
       RunnableFuture<T> ftask = newTaskFor(task);//把Callable对象封装成RunnableFuture对象。
       execute(ftask);//执行execute方法
       return ftask;//返回一个Future对象。
   }

在AbstractExecutorService里面的实现如上。在学习线程池的时候,学习过execute方法的基本执行过程,也知道它的参数是一个Runnable类型的command。因此 RunnableFuture肯定是Runnable的一个子类。 返回的是一个Future对象,那么它应该也是Future的子类。这名字取的也是够直白。把Callable对象封装成RunnableFuture调用的是 newTaskFor
newTaskFor

   protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }

调用构造方法

RunnableFuture

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

注意:这里多继承是因为是接口的多继承。JAVA中只有类是单继承。别误会
Future

//取消任务,任务我们都知道有三种基本情况,已经执行完毕,正在执行,还未执行。
//1.如果任务已经执行完毕,那么不管mayInterruptIfRunning是什么返回false
//2.任务未执行,不管mayInterruptIfRunning方法都返回true
//3.任务正在执行,那么返回的就是mayInterruptIfRunning
boolean cancel(boolean mayInterruptIfRunning);
//判断任务是否被取消,如果在任务正常完成前被取消成功,则返回 true。
boolean isCancelled();
//任务是否完成,已经完成返回true
boolean isDone();
//获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
V get() throws InterruptedException, ExecutionException;
获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;

再来看看RunnableFuture接口的实现类:
FutureTask

    /**
     * Possible state transitions:状态的四种变化
     * 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;//被中断

构造方法

  public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

FutureTask实现了RunnableFuture,RunnableFuture继承了Runnable接口,所以FutureTask实现run方法
run方法

public void run() {
      /** 
        *首先判断任务的状态 如果任务的状态不是new 说明任务的状态已经改变(说明他已经走了4种可能变化的一种) 
        * 如果状态是new就会把 当前执行任务的线程付给runner, 这里用的cmpandset如果runner不为空 说明已经有线程在执行 
    * 任务也会退出执行,如果状态是new并且runner为空并且把当前的线程付给了runner那么就继续执行任务(runner state 都是 volatile 
        *类型的变量是一个很轻量机的线程安全操作) 
        *引起state状态变化的原因 就是调用了cancel 或是 run 
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {  //如果是新建状态,并且c不等于空就开始执行任务
            V result;
            boolean ran;
            try {
                result = c.call(); //调用callable对象的call方法
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);   //如果正常执行完毕,设置结果
        }
    } finally {

        runner = null;

        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

总结
在多线程环境中,通过任务实现Callable接口,然后调用线程池的submit方法,submit方法分两步

  1. 把Callable对象封装成一个FutureTask对象。(实际就是在FutureTask中有一个Callable类型的属性。)FutureTask类是RunnableFuture的实现类,RunnableFuture继承了Runnable和Future接口。在FutureTask实现了Runnable方法的run方法。run方法中对调用callable方法的call方法,然后把结果设置给FutureTask的另外一个属性。
  2. 调用线程池的execute方法,等待执行FutrueTask中的run方法。

当然这里只是总结一个正确的运行结果。其中可能任务会被取消,线程发生中断,异常等等情况。可以参照源码学习。其它的情况都只是一个上面状态的一个变化过程而已。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值