Callable 接口支持返回值的多线程编程

2020-11-15 0 By admin

无论是从 Thread 类继承还是实现 Runnable 接口来创建线程,方法 run() 是没有返回值的。带返回值的线程可以通过 Callable 接口来定义,通过 Future 接口来获得线程的返回值。此接口为 JDK 5.0 新增。

一、Callable 接口

接口 Callable 和 Runnable 类似,其定义的一般形式:

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

其中,类型参数 V 指明了线程返回值的类型。Callable<Integer> 将返回一个 Integer 型的值。
使用接口 Callable 创建的线程必须实现 call() 方法,call() 方法的返回值类型由 V 来指定。

  1. call()可以有返回值的。
  2. call()可以抛出异常,被外面的操作捕获,获取异常的信息。
  3. Callable是支持泛型的。
  4. 需要借助FutureTask类,比如获取返回结果。

二、Future 接口

Future 接口允许在未来某个时间获得线程运行的结果,它保存了使用 Callable 接口定义的线程异步运行的结果。当启动一个 Future 对象后,相当于启动了一个计算,Future 对象的计算结果在计算好后得到。
Future 接口可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。

Future 接口的常用方法:

  1. V get()方法:用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
  2. V get(long timeout, TimeUnit unit):用来获取执行结果,如果在指定时间内,还没获取到结果,则抛出 TimeOutException 异常。
  3. boolean cancel(boolean mayInterrupt) :尝试取消任务的运行。
  4. boolean isCancelled: 判断是否被取消。
  5. boolean isDone: 判断任务是否已经完成。

三、FutureTask 包装器

FutrueTask 是 Futrue接口的唯一的实现类。
FutureTask 包装器同时实现了接口 Runnable 和 Callable,它可以很方便地对 Callable 对象进行封装并转换成 Future 对象。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值

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

3.1、Callable 和 Runnable 比较

  1. Callable 是有返回值的,而 Runnable 没有返回值。
  2. Callable 执行入口方法是 call() 方法,Runnable 的执行入口方法是 run()。
  3. call() 方法可以抛出异常,run() 方法不可以。
  4. 运行 Callable 相当于启动了一个异步计算,将来在通过 Future 得到计算的结果;而且可以使用 Future 的 cancel() 方法取消方法的执行。

四、代码示例

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//1.创建一个实现Callable的实现类
class NumThread implements Callable{
  //2.实现call方法,将此线程需要执行的操作声明在call()中
  @Override
  public Object call() throws Exception {
    int sum = 0;
    for(int i = 1;i <= 100;i++){
      if(i % 2 == 0){
        System.out.println(i);
        sum += i;
      }
    }
    return sum;
  }
}
public class ThreadNew {
  public static void main(String[] args) {
    //3.创建Callable接口实现类的对象
    NumThread numThread = new NumThread();
    //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
    FutureTask futureTask = new FutureTask(numThread);
    //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
    new Thread(futureTask).start();
    try {
      //6.获取Callable中call方法的返回值
      //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
      Object sum = futureTask.get();
      System.out.println("总和为:" + sum);
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }
  }
}