Callable、Future、FutureTask之间的关系以及Callable与Runnable的区别

Callable、Future、FutureTask之间的关系

Callable Future 是在 JAVA 的后续版本中引入进来的, Callable 类似于 Runnable 接口,实现 Callable 接口的类与实现Runnable 的类都是可以被线程执行的任务。
  • Callable是Runnable封装的异步运算任务
  • Future接收Callable异步运算结果
  • FutureTask封装Future的实现类

Callable与Runnable的区别

  • Callable定义的方法是call(),Runnable定义的方法是run()
  • Callable可以抛出异常,Runnable不可以抛出异常
  • Callable有返回值,Runnable无返回值

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

FutureTask

FutureTask是Future的实现类,是可以取消异步计算的,在任务完成时得到计算结果,没完成时使用get()方法阻塞等待结果。

FutureTask不仅实现了Futrue接口,还实现了Runnable接口,也就是说即可以当作一个任务交给Exectutor来进行运算,还可以通过Thread来将任务交给线程。

将任务交给Thread线程来运行

public class Cdemo1 implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("callable is coming");
        Thread.sleep(5000);
        return new Random().nextInt();
    }
}
public class demo1 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
         Cdemo1 demo=new Cdemo1();
        FutureTask future=new FutureTask(demo);
        Thread thread=new Thread(future);
        thread.start();
        Thread.sleep(2000);
        //取消任务
  /**   如果取消任务,当前取消的任务已完成,则取消失败返回false;当前取消的任务未完成,则取消成 
        功返回true
         根据任务中代码在取消任务时,还在休眠的状态
         */
        future.cancel(true);
        //判断是否在任务正常完成前取消 
        /**判断任务是否取消,如果在任务完成前取消了,那就是返回true
         */
        System.out.println("future is cancel:" + future.isCancelled());
        if(!future.isCancelled()){
            System.out.println("futrue is cancelled");
        }
       /** 判断任务是否完成,完成就返回false,完成的情况有:正常完成终结,异常以及取消任务完成
       */
        System.out.println("future is done:" + future.isDone());
        if(!future.isDone()){
            System.out.println("接收的结果:"+future.get());
        }else {
            System.out.println("future is done!");
        }
    }
}

将任务放入到线程池中

public class FutureCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("callable is coming:"+System.currentTimeMillis());
        Thread.sleep(10000);
       return "任务已执行完毕";
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Callable<String> callable=new FutureCallable();
        ExecutorService service= Executors.newSingleThreadExecutor();
        Future<String> future=service.submit(callable);
//shutdown执行完成后,关闭线程池,只是不接受新的任务,但是会将已有的任务执行完毕
        service.shutdown();
        Thread.sleep(5000);
        System.out.println("休眠5s:"+System.currentTimeMillis());
        String str= future.get();
        System.out.println("future已拿到时间"+System.currentTimeMillis()+"内容:"+str);
    }
}

基本相差10s,是因为在线程中任务还未执行完成时,Future中的get()方法会阻塞主线程,一直等得任务完成后,才接收结果并继续执行。 

 

 使用线程池的情况下当程序结束时记得调用shutdown关闭线程池

如果不调用shutdown()方法来关闭线程池,那么在主线程执行结束后,JVM仍存在。因为只要有用户线程,JVM就是不退出的,线程池中ThreadFactory创建的都是用户线程(用户线程就是独立于操作系统的线程,不依赖于操作系统的核心,main就是用户线程), 通过Thread.setDaemon(false)可以设置为用户线程,ThreadFactory中创建的核心线程是一直存在的,当没有任务之后,核心线程空闲,就会被阻塞,那么JVM就会一直存在。

使用完线程池不调用shutdown方法会导致线程池的线程资源一直不被释放。

线程池使用FutureTask时需要注意的事情

线程池使用FutureTask时,如果拒绝策略使用了DiscardPolicy(放弃任务)和DiscardOldPolicy(放弃队列中最早的任务,用新的进入的任务替代),被拒绝的任务的Future对象上调用了get()方法,那么可能线程会一直陷入阻塞状态。

以DiscardPolicy为例,在使用拒绝策略DiscardPolicy时,并没有设置Future的状态,那么状态就是等于0,为NEW,而且一直为NEW,在调用get()方法时,会比较Futrue的状态,如果大于等于COMPLETING(1)时,就会阻塞,只有状态值等于NORMAL时才会正常返回。

所以在实际开发过程中,尽量使用带超时的get()方法,以避免线程的阻塞。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值