callable,runnable,future和futureTask详解

之前的文章我们在讲解的时候说到新建线程的两种方式,一种是直接继承Thread类,重写run方法,另外一种是继承Runnable接口。但是这两种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。于是java后面又增加了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果

  1. Runnable和Callable
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

Runnable只是一个接口,里面只是声明了一个run方法,可以看到方法的返回值是void,所以线程执行完了没有任何的返回值

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

Callable也是一个接口,它里面声明了一个call方法,可以看到它是一个泛型接口,call()函数返回的类型就是传递进来的V类型。线程的执行是异步的,一个线程和另外一个线程的执行是互不干扰的,所以你不可能从别的线程中获得返回值,所以要想获得Callable的返回值就需要用到Future这个接口,Futrue可以监视目标线程调用call的情况,当你调用Future的get()方法以获得结果时,当前线程就开始阻塞,直接call方法结束返回结果。

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

下面我们讲解下这五个方法的作用
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方法的线程,直到get方法返回结果才能继续向下执行,如果get方法一直没有返回值,那么当前线程会一直阻塞下去

get(long timeout, TimeUnit unit)方法:获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null,这个就避免了一直获取不到结果使得当前线程一直阻塞的情况发生

  1. FutureTask

首先看下FutureTask的继承关系图
在这里插入图片描述
可以看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
FutureTask内部的几种状态值

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;

再根据注释,可以得知当创建一个 FutureTask对象时,初始状态是 NEW,在运行过程中,运行状态仅在方法set,setException和 cancel 中转换为终端状态。有四种状态转换过程:


NEW -> COMPLETING -> NORMAL:正常执行并返回结果(run 执行成功再设置状态为 COMPLETING)

NEW -> COMPLETING -> EXCEPTIONAL:执行过程中出现异常(setException 先设置状态为 COMPLETING)

NEW -> CANCELLED:执行前被取消

NEW -> INTERRUPTING -> INTERRUPTED:执行时被中断(cancel 参数为 true 才可能出现这个状态)

FutureTask构造器

//传入的参数是Callable具有返回值,这就说明可以通过FutureTask获取Callable的返回值
public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    //将传入的callable赋值给callable
    this.callable = callable;
    //将初始状态设置为new状态
    this.state = NEW;       // ensure visibility of callable
}
//这个传入d额是Runnable,通过Executors.callable方法转换成Callable可以进行返回值
public FutureTask(Runnable runnable, V result) {
     //将Runnable转换成Callable
    this.callable = Executors.callable(runnable, result);
    //将初始状态设置为new状态
    this.state = NEW;       // ensure visibility of callable
}
  1. 示例代码
    Future
    第一种方式是使用继承了ExecutorService的线程池ThreadPoolExecutor中的submit方法,将Callable直接提交创建Future。
import java.util.concurrent.*;
public class FutureExample {
    static class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            System.out.println("do something in callable");
            Thread.sleep(5000);
            return "Ok";
        }
    }
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Future<String> future = executorService.submit(new MyCallable());
        System.out.println("do something in main");
        Thread.sleep(1000);
        String result = future.get();
        System.out.println("result: " + result);
    }
}

FutureTask
第二种方法是创建一个写好Callable的FutureTask对象实例,再submit。因为FutureTask内部本身拥有run方法,也可以直接创建线程Thread运行。
利用 ExecutorService

import java.util.concurrent.*
public class FutureTaskWithExecutorService {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        FutureTask<String> futureTask = new FutureTask<>(() -> { // java 8 函数式写法
            System.out.println("do something in callable");
            Thread.sleep(5000);
            return "Ok";
        });
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.submit(futureTask);
        System.out.println("do something in main");
        Thread.sleep(1000);
        String result = futureTask.get();
        System.out.println("result: " + result);
    }
}

利用 Thread

import java.util.concurrent.*
public class FutureTaskWithThread {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("do something in callable");
                Thread.sleep(5000);
                return "Ok";
            }
        });
        new Thread(futureTask).start();
        System.out.println("do something in main");
        Thread.sleep(1000);
        String result = futureTask.get();
        System.out.println("result: " + result);
    }
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值