多线程编程Future与Callable、FutureTask浅析了解

96 阅读4分钟

我们知道,创建线程有两种方式,一种是实现了Runnable接口,而另一种就是继承Thread了。但是我们也知道,这两种在任务执行完之后是没有办法获取返回结果的。那如果线程一多起来,就根本不知道哪一个出的问题,相对的排查错误的工作量就大了。那么有把线程返回结果的方法有吗?

有的,那就是Callable和Future了。通过他们构建线程,执行结束后就可以获取到了执行结果。

Callable<V>接口

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

这时候不得认识一下ExcutorService接口,这个接口不管是Runnable接口还是Callable接口都是可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor所执行的,这两个都是基于ExcutorService实现的。

ExcutorService的方法

<T> Future<T> submit(Callable<T> task);

可以看到实现了Callable接口,并且的话返回了结果Future

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

实现了Runnable接口,并且调用Future返回的result对象

Future<?> submit(Runnable task);

实现了Runnable接口,并且返回了Future结果。

Future<V>接口

上面的三个方法都提到了Future,那它是干啥的呢?Future接口它是用来获取异步计算出来的结果,说简单点就是用于对Runnable和Callable对象执行出来的结果进行获取,判断是否已经完成等等操作。看源码!

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;
}
  • boolean cancel:  这个方法是说明如果任务还没有开始执行的话,执行该方法会返回false;但如果任务已经是启动了,执行该方法(mayInterruptIfRunning=true)将会中断掉执行该任务的线程来尝试停止任务,如果任务停止成功的话,那么返回的是true;还有另一种情况就是如果任务已经启动,执行该方法(mayInterruptIfRunning=false)将不会对正在执行中的任务线程产生影响,它会让线程执行到完成,这个时候返回false。
  • boolean isCancelled():  如果在任务完成之前被取消掉的话,就返回true。
  • boolean isDone():  任务执行结果,但不管任务是正常执行结束的还是在中途取消执行还或者是出现异常都会返回true。
  • V get():  用来获取异步执行出来的结果,如果出现结果出现不可用,那么这个方法就会阻塞,一直到异步的计算完成。
  • V get(long timeout, TimeUnit unit):  获取异步执行的结果,和get一样会阻塞,但是阻塞会有时间的限制,超过时间限制的话,将会抛出异常。

FutureTask

通过了上面的分析,明白Future它只是一个接口,它是不能够直接创建对象的,所以FutureTask出现了。

public class FutureTask<Vimplements RunnableFuture<V> {
    
public interface RunnableFuture<Vextends RunnableFuture<V> {
    void run();
}

可以看出FutureTask除了实现Future接口还实现Runnable接口,所以的话FutureTask也是可以直接把任务给Executor执行的。接下来分析一下FutureTask.run执行的三种状态。

  • 第一种: 未启动,在run没有执行前,FutureTask它是处于未启动的状态。
  • 第二种: 已启动,run在执行的过程中,FutureTask是已经在启动的状态了。
  • 第三种:已完成,run方法执行完成结束,FutureTask是在完成的状态。

图片

FutureTask构造函数

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

Callable<V>/Future<V>/FutureTask的使用

使用Callable+Future获取执行结果

\

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class CallableDemo 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子线程计算结束!");
        return sum;
    }


    public static void main(String[] args) {
        //创建线程池
        ExecutorService es = Executors.newSingleThreadExecutor();
        //创建Callable对象任务
        CallableDemo calTask = new CallableDemo();
        //提交任务并获取执行结果
        Future<Integer> future = es.submit(calTask);
        //关闭线程池
        es.shutdown();
        try {
            Thread.sleep(2000);
            System.out.println("主线程在执行其他任务");

            if (future.get() != null) {
                //输出获取到的结果
                System.out.println("future.get()-->" + future.get());
            } else {
                //输出获取到的结果
                System.out.println("future.get()未获取到结果");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("主线程在执行完成");
    }
}

图片


使用Callable+FutureTask获取执行结果

\

import java.util.concurrent.*;

class CallableDemo 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子线程计算结束!");
        return sum;
    }

public static void main(String[] args) {
        
        //创建线程池
        ExecutorService es = Executors.newSingleThreadExecutor();
        //创建Callable对象任务
        CallableDemo calTask=new CallableDemo();
        //创建FutureTask
        FutureTask<Integer> futureTask=new FutureTask<>(calTask);
        //执行任务
        es.submit(futureTask);
        //关闭线程池
        es.shutdown();
        try {
        Thread.sleep(2000);
        System.out.println("主线程在执行其他任务");

        if(futureTask.get()!=null){
        //输出获取到的结果
        System.out.println("futureTask.get()-->"+futureTask.get());
        }else{
        //输出获取到的结果
        System.out.println("futureTask.get()未获取到结果");
        }

        } catch (Exception e) {
        e.printStackTrace();
        }
        System.out.println("主线程在执行完成");
        }}

图片

总结:

  • Callable与Runable功能相似,Callable的call有返回值,可以返回给客户端,而Runable没有返回值,一般情况下,Callable与FutureTask一起使用,或者通过线程池的submit方法返回相应的Future\

  • Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果操作。get方法会阻塞,直到任务返回结果\

  • FutureTask则是一个RunnableFuture,而RunnableFuture实现了Runnbale又实现了Futrue这两个接口