callable详解

65 阅读4分钟

创建线程常见的有三种方式,一种是继承Thread类,一种是实现Runnable接口,最后一种就是Callable,今天主要是对最后不常见的Callable方式进行介绍。

为什么要使用callable接口?

  • 与使用Runnable接口相比,Callable功能更加强大些
  • 相比run() 方法,可以有返回值
  • 方法可以抛出异常
  • 支持泛型的返回值(需要借助FutureTask类,获取返回结果)
  • Callable规定的方法是call(),Runnable规定的方法是run()
  //Callable 接口
  public interface Callable<V> {
     V call() throws Exception;
  }
  // Runnable 接口
  public interface Runnable {
      public abstract void run();
  }
  • 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可cancel()取消任务的执行,还可get()获取执行结果
  • 缺点:在获取分线程执行结果的时候,当前线程(或是主线程)受阻塞,效率较低。

callable接口基本使用

创建线程

创建一个类事项Callable接口:

class myCalls implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        return null;
    }
}

使用线程

先看一下Thread的构造方法:

public Thread() {}
public Thread(Runnable target) {}
Thread(Runnable target, AccessControlContext acc) {}
public Thread(ThreadGroup group, Runnable target) {}
public Thread(String name) {}
public Thread(ThreadGroup group, String name) {}
public Thread(Runnable target, String name) {}
public Thread(ThreadGroup group, Runnable target, String name) {}
public Thread(ThreadGroup group, Runnable target, String name,
     long stackSize) {}

这个源码我摘自jdk1.8,一共列举了9个构造函数,但是仔细观察就能发现,**没有一个构造方法可以传入Callable接口,**这也就意味着不能根据之前那种简单的方式来创建线程。所以既然线程能有返回值,不知道是否可以联想到一个函数式接口Future

Future

//1、FutureTask实现了RunnableFuture
public class FutureTask<V> implements RunnableFuture<V>
//2、RunnableFuture又是继承了Runnable和Future
public interface RunnableFuture<V> extends Runnable, Future<V>
//3、Future接口的常用方法
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;
}

常用方法:

  • cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。

    参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。

    • 如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;
    • 如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,
    • 若mayInterruptIfRunning设置为false,则返回false;
    • 如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
  • isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。

  • isDone方法表示任务是否已经完成,若任务完成,则返回true;

  • get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;

  • get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

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

  • FutureTask可以用来包装Callable或者Runnbale对象。因为FutureTask实现了Runnable接口,所以FutureTask也可以被提交给Executor

所以就可以配合Callable进行使用了:

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

public class Dmoe2 {
    public static void main(String[] args) {
        myCalls myCalls=new myCalls();
        FutureTask<Integer> test=new FutureTask<>(myCalls);
        Thread thread=new Thread(test,"线程一");
        thread.start();
        //获取返回的结果
        try {
            System.out.println("执行的结果为:"+test.get());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }
}
class myCalls implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"进入到了Callable");
        int sum = 0;
        for (int i = 0;i < 5; i++) {
            sum = sum + i;
        }
        return sum;

}
}

运行结果:

线程一进入到了Callable 执行的结果为:10

配合线程池使用

public class Test3 {
	public static void main(String[] args) throws Exception{
        ExecutorService executor = Executors.newCachedThreadPool();
        FutureTask<Integer> futureTask = new FutureTask<Integer>
        								(new MyCallable());
        executor.submit(futureTask);
        executor.shutdown();
		//线程运行结束,说明Callable接口的方法已经完成,此时我们就可以获取返回值
		System.out.println("Callable返回的结果是:"+futureTask.get());
	}
}

基本使用流程

Callable1111.png