Java并发工具之Future和Callable

1. Runnable的缺陷

  • 不能返回一个返回值;
  • 不能在方法声明异常;
  • 为什么设计成这样:即便是声明异常,也没有处理它的类,所以没必要这么设计;

2. Callable接口

  • 类似于Runnable;
  • 实现call方法;
  • 有返回值;

3. Future类

  • 作用:遇到耗时的方法,用子线程去执行,本身去执行其他的事情;
  • Callable和Future的关系:可以用Future.get来获取Call的执行结果,还可以利用Future.isDone方法来判断任务是否已经执行完毕,以及取消这个任务,限时获取任务的结果等;
  • 当call方法未执行完毕之前,调用get的线程会被阻塞,直到call方法返回了结果后,此时future.get方法才会得到该结果,然后主线程才会切换到RUNNABLE状态;
  • 所以Future是一个存储器,存储了call这个任务的结果,而这个任务的执行时间是无法确定的,因为完全取决于call方法执行的情况;

4. Future的主要方法:5个

  • get():获取结果;

  • get(timeout):超时不获取结果;

  • cancel():取消任务的执行;

     1. 如果这个任务还没有开始执行,那么这种情况最简单,任务会被正常的取消,未来也不会被执行,方法返回true;
     2. 如果任务已完成,或者已取消:那么cancel方法会执行失败,返回false;
     3. 如果这个任务已经执行一半了,会根据传入的参数mayInterruptIfRunning判断,true:中断正在运行的线程,false:不进行中断,既然已经执行一半了,就等他执行完毕;
     cancel(true)适用于任务能处理interrupte的情景;cancel(false)仅仅用于避免启动尚未启动的任务;
    
  • isDone():判断线程执行完毕;

  • isCancelled():判断任务是不是被取消了;

5. 用法一:用线程池的submit方法提交Callable对象返回Future对象

  • 首先,用线程池提交Callable任务,提交时线程池会立刻返回给我们一个空的Future容器。当线程的任务一旦执行完毕,也就是当我们可以获取结果的时候,线程池便会把该结果填入到之前我们的那个Future中去(而不是创建一个新的Future),我们此时就可以从该Future中获得任务执行的结果;
public class FutureCallableDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        Future<Integer> future = executorService.submit(new CallableTask());
        System.out.println(future.get());
        System.out.println("future等待完毕");
        executorService.shutdown();
    }
    static class CallableTask implements Callable {

        @Override
        public Integer call() throws Exception {
            Thread.sleep(3000);
            return new Random().nextInt();
        }
    }
}
1019197109
future等待完毕

6. 用法二:用FutureTask来创建Future

  • 用FutureTask来获取Future和任务的结果;
  • FutureTask是一种包装器,可以把Callable转化成Future和Runnable,它同时实现二者的接口;
  • 在这里插入图片描述
    所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值;

调用方法如下:

  • 把Callable实例作为参数,生成FutureTask的对象,此时已经把任务内容传了进入;
  • 因为FutureTask实现了Runnable接口,所以可以把这个对象当做一个Runnable对象;
  • 用线程池或者另起线程来执行这个FutureTask对象;
  • 在启动线程后,FutureTask对象的run方法中会调用Callable对象的call方法返回任务结果;
  • 因为FutureTask实现了Future接口,所以可以通过FutureTask获取刚才执行的结果;
public class FutureTaskDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Callable> callableFutureTask = new FutureTask<Callable>(new CallableTask());
        Thread thread = new Thread(callableFutureTask);
        thread.start();
        System.out.println(callableFutureTask.get());
    }

    static class CallableTask implements Callable {

        @Override
        public String call() throws Exception {
            return "我是call方法的执行结果";
        }
    }
}
我是call方法的执行结果

7. Future使用注意点

  • Future的生命周期不能后退,就像线程池的生命周期一样,一旦完成了任务,不能重头再来;
  • 当for循环批量获取future的结果时,容易发生一部分线程很慢的情况,Future.get()方法一旦发生阻塞,后续的线程要想获取结果也必须得等待,即便是后面的任务已经运行完了,也必须得等第一个线程get到结果才行,可以利用get(timeout)限制,一旦超时就舍弃这个结果(会抛出超时异常);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值