Callable and FutureTask

本文详细介绍了Java中的Callable接口及其与Runnable的区别,重点讲解了FutureTask如何包装Callable对象并实现异步执行和回调。此外,还探讨了Future接口的方法如cancel(),isCancelled(),isDone(),get()以及get()的超时处理和线程池的使用。
摘要由CSDN通过智能技术生成

Callable

由关系图可知,Callable和Runnable一样,也是一个函数式接口,可以使用Lambda表达式

与之不同的是,其内部的call()方法可以抛出异常且能return一个返回值

 Callable<Object> callable = new Callable() {
            @Override
            public Object call() throws Exception {
                System.out.println("执行call方法");
                return null;
            }
        };

FutureTask

FutureTask构造方法源码:

public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

 public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

Future接口方法

 //mayInterruptIfRunning:是否取消正在执行但还有执行完毕的任务 
 //true:取消任务 返回true
 //false:让任务执行完毕 返回false
 boolean cancel(boolean mayInterruptIfRunning);
 
 //任务是否被取消
 boolean isCancelled();

 //任务是否完成 
 //完成可能是由于正常终止、异常或取消 --- 在所有这些情况下,此方法将返回 true
 boolean isDone();

 //得到任务返回值,如果未执行的话会阻塞等待
 V get() throws InterruptedException, ExecutionException;

 //设置最大阻塞时间,如果超过最大阻塞时间后还是没有执行完任务,抛出TimeOutException
 V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;

根据上方关系图我们可知

FutureTask可以包装Runnable/Callable对象-----构造方法

FutureTask实现了Runnable接口 ,故其可以作为Runnable被线程执行

notice:线程执行的是FutureTask类包装的Callable对象下的call()方法

public static void main(String[] args) throws ExecutionException, InterruptedException {
        //使用Callable进行1-100相加数和
        Callable<Long> callable = new Callable<Long>() {
            @Override
            public Long call() throws Exception {
                System.out.println("开始执行子线程相加内容");
                long sum = 0;
                for(int i = 1; i <= 100; i++){
                    sum += i;
                }
                return sum;
            }
        };
        
        FutureTask<Long> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask);

        //真正执行call方法的是线程 所以必须要启动线程后 call方法才会执行
        //t.start();

        //相当于t.join操作
        System.out.println("futureTask.get()得到call方法的函数返回值:"+futureTask.get()); //如果没有call方法内容没有执行完 get方法会一直阻塞
}

此时没有执行t.start操作,futureTask.get()方法就会一直阻塞

反之执行t.start;


 
  //使用Callable进行1-100相加数和
        Callable<Long> callable = new Callable<Long>() {
            @Override
            public Long call() throws Exception {
                Thread.sleep(3000);
                System.out.println("开始执行子线程相加内容");
                long sum = 0;
                for(int i = 1; i <= 100; i++){
                    sum += i;
                }
                return sum;
            }
        };

        FutureTask<Long> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask);

        //真正执行call方法的是线程 所以必须要启动线程后 call方法才会执行
        t.start();
        long begin = System.currentTimeMillis();

        //相当于t.join操作
        System.out.println("futureTask.get()得到call方法的函数返回值:"+futureTask.get()); //如果没有call方法内容没有执行完 get方法会一直阻塞

        long end = System.currentTimeMillis();
        System.out.println("阻塞时间:"+ (end -begin));
    }

上述代码中,我们在call方法中休眠了三秒,由于当get方法任务没有执行完时,就会阻塞,故主线程被三秒,所以get()操作类似于join方法


get():

我们知道,当线程在休眠时被终止时,会触发sleep内部异常,在Runnable中,触发异常后并不会终止进程,而是将线程提前唤醒,且将终止标志位重新设置为false,后续的操作由程序员的代码决定 

public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < 3; i++){
                    System.out.println("Hello World");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        System.out.println("触发skeep interrupted异常 后续执行操作由我决定");
                    }
                }

            }
        });
        t1.start();
        t1.interrupt();
        System.out.println("线程是否被终止:"+t1.isInterrupted());
    }

即使触发了异常,但还是继续完成了runnable中的run方法


但在Callable中,如果线程被休眠时被唤醒,FutureTask.get()方法就会直接报错且退出程序

public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("你好");
                Thread.sleep(1000);
                return "我要报错拉";
            }
        };
        FutureTask task = new FutureTask(callable);
        Thread t = new Thread(task);
        t.start();
        t.interrupt();
        //非正常终止 --- 休眠时终止
        System.out.println("isDone:"+task.isDone());
        System.out.println(task.get());
    }


但如果仅仅是启动线程(没有FutureTask.get()操作),那么不会报错,但会直接终止线程

 public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                for(int i = 0; i < 3; i++){
                    System.out.println("你好");
                    Thread.sleep(1000);
                }
                return "我要报错拉";
            }
        };
        FutureTask task = new FutureTask(callable);
        Thread t = new Thread(task);
        t.start();
        t.interrupt();
        //非正常终止 --- 休眠时终止
        System.out.println("isDone:"+task.isDone());
    }


超时get():
 public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        //超时get方法
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(2000);
                System.out.println("任务继续执行");
                return "ok";
            }
        };
        FutureTask futureTask = new FutureTask(callable);
        Thread t = new Thread(futureTask);
        t.start();
        //若超出时间 报TimeoutException异常 call方法中的内容继续进行  
        //无法取到call方法中的返回值了
        System.out.println(futureTask.get(1, TimeUnit.SECONDS));
    }

.

超时后call方法中的任务还是会继续进行,但无法通过get方法获得call方法的返回值了


cancel():
public static void main(String[] args) {
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for(int i = 0; i <= 10; i++){
                    sum += i;
                }
                return sum;
            }
        };
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask);
        t.start();

        //取消任务  参数mayInterruptIfRunning是否取消正在运行的任务  其内部实际上还是通过interrupt方法
        System.out.println("cancel:"+futureTask.cancel(true));

        //任务是否被取消
        System.out.println("isCancelled:"+futureTask.isCancelled());

        //任务是否执行完成
        //如果此任务已完成,则返回 true。完成可能是由于正常终止、异常或取消 -- 在所有这些情况下,此方法将返回 true
        System.out.println("isDone:"+futureTask.isDone());
    }


 //取消任务  参数mayInterruptIfRunning是否取消正在运行的任务  其内部实际上还是通过interrupt方法
 System.out.println("cancel:"+futureTask.cancel(true));

 //在取消任务后 调用get方法 此时不能返回call方法返回值 且会抛出异常 退出程序
 System.out.println("after cancel:"+ futureTask.get());


使用线程池执行Callable
//利用线程池执行
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(10);

        //execute Runnable command
        //submit Future<V> future
        //这里是Callable 只能使用submit Runnable/Callable都可以 返回值是Future<?> / 不能使用execute:Runnable
        Future<String> future = service.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "Hello World";
            }
        });
        System.out.println(future.get());
        service.shutdown();
    }

  • 11
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值