美文网首页
Java并发编程 Future和Callable

Java并发编程 Future和Callable

作者: 香沙小熊 | 来源:发表于2020-03-16 14:52 被阅读0次

    1.Runnable的缺陷

    • 不能返回一个返回值
    • 也不能抛出checked Exception


      image.png
    为什么有这样的缺陷?

    因为run()方法是Runnable接口里面的方法,而Runnable接口在定义run()方法的时候没有抛出任何异常,所以子类在重写run()方法的时候要小于或等于父类(Runnable)的run()方法的异常,所以父类没有抛出异常,子类不能抛出异常

    2.Callable接口

    • 类似于Runnable,被其它线程执行的任务
    • 实现call方法
    • 有返回值
    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;
    }
    

    3.Future类

    3.1 Future的作用

    Future表示一个可能还没有完成的异步任务的结果,针对这个结果可以添加Callback以便在任务执行成功或失败后作出相应的操作。

    3.2 Callable和Future的关系
    1. 我们可以用Future.get来获取Callable接口返回的执行结果,还可以通过Future.isDone()来判断任务是否已经执行完了。以及取消这个任务,限时获取任务的结果等。
    2. 在call()未执行完毕之前,调用get()的线程(假定此时是主线程)会被阻塞,直到call()方法返回了结果后,此时future.get()才会得到该结果,然后主线程才会切换到runnable状态。
      3.所以Future是一个存储器,它存储了call()这个任务的结果,而这个任务的执行时间是无法提前确定的,因为这完全取决于call()方法执行的情况。
    3.3 Future方法
    boolean cancel (boolean mayInterruptIfRunning) 取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束
    boolean isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 true
    boolean isDone () 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true
    V get () throws InterruptedException, ExecutionException 等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出CancellationException

    任务正常完成:get方法会立刻返回结果
    任务尚未完成:get将阻塞并直到任务完成

    V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计算超时,将抛出TimeoutException

    4.线程池的submit方法返回Future对象

    单个任务

    public class OneFuture {
    
        public static void main(String[] args) {
            ExecutorService service = Executors.newFixedThreadPool(10);
            Future<Integer> future = service.submit(new CallableTask());
            try {
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            service.shutdown();
        }
    
        static class CallableTask implements Callable<Integer> {
    
            @Override
            public Integer call() throws Exception {
                Thread.sleep(3000);
                return new Random().nextInt();
            }
        }
    
    }
    
    1709866210
    

    多个任务

    public class MultiFutures {
    
        public static void main(String[] args) throws InterruptedException {
            ExecutorService service = Executors.newFixedThreadPool(20);
            ArrayList<Future> futures = new ArrayList<>();
            for (int i = 0; i < 20; i++) {
                Future<Integer> future = service.submit(new CallableTask());
                futures.add(future);
            }
            for (int i = 0; i < 20; i++) {
                Future<Integer> future = futures.get(i);
                try {
                    Integer integer = future.get();
                    System.out.println(integer);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }
    
        static class CallableTask implements Callable<Integer> {
    
            @Override
            public Integer call() throws Exception {
                Thread.sleep(3000);
                return new Random().nextInt();
            }
        }
    }
    
    public class GetException {
    
        public static void main(String[] args) {
            ExecutorService service = Executors.newFixedThreadPool(20);
            Future<Integer> future = service.submit(new CallableTask());
    
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println(i);
                    Thread.sleep(500);
                }
                System.out.println(future.isDone());
                future.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("InterruptedException异常");
            } catch (ExecutionException e) {
                e.printStackTrace();
                System.out.println("ExecutionException异常");
            }
        }
    
        static class CallableTask implements Callable<Integer> {
    
            @Override
            public Integer call() throws Exception {
                throw new IllegalArgumentException("Callable抛出异常");
            }
        }
    }
    
    
    image.png
    结论:并不是说一产生异常就抛出,直到我们get执行时,才会抛出。
    演示cancel传入true和false的区别,代表是否中断正在执行的任务。
    public class Timeout {
    
        private static final Ad DEFAULT_AD = new Ad("无网络时候的默认广告");
        private static final ExecutorService exec = Executors.newFixedThreadPool(10);
        @Data
        static class Ad {
            String name;
            public Ad(String name) {
                this.name = name;
            }
        }
    
    
        static class FetchAdTask implements Callable<Ad> {
    
            @Override
            public Ad call() throws Exception {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    System.out.println("sleep期间被中断了");
                    return new Ad("被中断时候的默认广告");
                }
                return new Ad("旅游订票哪家强?找某程");
            }
        }
    
    
        public void printAd() {
            Future<Ad> f = exec.submit(new FetchAdTask());
            Ad ad;
            try {
                ad = f.get(2000, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                ad = new Ad("被中断时候的默认广告");
            } catch (ExecutionException e) {
                ad = new Ad("异常时候的默认广告");
            } catch (TimeoutException e) {
                ad = new Ad("超时时候的默认广告");
                System.out.println("超时,未获取到广告");
                boolean cancel = f.cancel(false);
                System.out.println("cancel的结果:" + cancel);
            }
            exec.shutdown();
            System.out.println(ad);
        }
    
        public static void main(String[] args) {
            Timeout timeout = new Timeout();
            timeout.printAd();
        }
    }
    
    超时,未获取到广告
    cancel的结果:true
    Timeout.Ad(name=超时时候的默认广告)
    
    修改 boolean cancel = f.cancel(true);
    超时,未获取到广告
    cancel的结果:true
    sleep期间被中断了
    Timeout.Ad(name=超时时候的默认广告)
    

    5.FutureTask来创建Future

    image.png
    public class FutureTaskDemo {
    
        public static void main(String[] args) {
            Task task = new Task();
            FutureTask<Integer> integerFutureTask = new FutureTask<>(task);
    //        new Thread(integerFutureTask).start();
            ExecutorService service = Executors.newCachedThreadPool();
            service.submit(integerFutureTask);
    
            try {
                System.out.println("task运行结果:"+integerFutureTask.get());
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
    
    class Task implements Callable<Integer> {
    
        @Override
        public Integer call() throws Exception {
            System.out.println("子线程正在计算");
            Thread.sleep(3000);
            int sum = 0;
            for (int i = 0; i < 100; i++) {
                sum += i;
            }
            return sum;
        }
    }
    
    子线程正在计算
    task运行结果:4950
    

    因为 FutureTask 实现了 Runnable,所以可将 FutureTask 提交给 Executor 执行。
    不像 Future<Integer> future = service.submit(new CallableTask());一样,要给赋值给左边future 变量。

    6.Future的注意点

    1. 当for循环批量获取future的结果时,容易发生一部分线程很慢的情况,get方法调用时应该使用timeout限制。
      采用CompletableFuture解决线程返回结果等待问题。
    2. Future的生命周期不能后退

    相关文章

      网友评论

          本文标题:Java并发编程 Future和Callable

          本文链接:https://www.haomeiwen.com/subject/bucvshtx.html