当前位置 博文首页 > 阳阳的博客:谈谈Runnable、Future、Callable、FutureTask之间的

    阳阳的博客:谈谈Runnable、Future、Callable、FutureTask之间的

    作者:[db:作者] 时间:2021-08-14 15:03

    注:该篇文章,只是用来梳理他们之间的关系,分析FutureTask的源码则会另开篇幅。


    创建线程的两种方式

    一种是实现Runnable接口,实现其run方式;另外一种是继承Thread类,重写其run方法。

    这一点可以从Thread类中的注释看到:


    Runnable

    Thread类也实现了Runnable接口,Runnable接口的源码如下:

    @FunctionalInterface
    public interface Runnable {
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see     java.lang.Thread#run()
         */
        public abstract void run();
    }

    因此,可以看得出,不管我们是实现Runnable接口,还是继承Thread类,run方式的返回类型都是void,我们都无法拿到线程运行的返回结果。

    如果非要拿到线程运行的结果怎么办?Callable便应运而生了。


    Callable

    @FunctionalInterface
    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还是一个函数式接口,其泛型参数类型与返回值类型一致。

    为了更清晰的说明他们之间的关系,这里先放上类图结构


    类图关系


    Future

    Callable接口,只是用于单纯地去获得返回值,但是什么时候去获取,或者查看线程运行的状态,就需要借助Future

    Future的源码如下:

    public interface Future<V> {
    
        //取消任务的执行,任务已经完成或者已经被取消时,调用此方法,会返回false
        boolean cancel(boolean mayInterruptIfRunning);
    
        //判断任务是否被取消
        boolean isCancelled();
    
        //判断任务是否完成(正常完成、异常以及被取消,都将返回true)
        boolean isDone();
    
        //阻塞地获取任务执行的结果
        V get() throws InterruptedException, ExecutionException;
    
        //在一定时间内,阻塞地获取任务执行的结果
        V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
    
    }
    

    可以看得出来,Future提供了取消任务、获取任务执行的状态与获取结果的能力。


    RunnableFuture

    public interface RunnableFuture<V> extends Runnable, Future<V> {
        /**
         * Sets this Future to the result of its computation
         * unless it has been cancelled.
         */
        void run();
    }

    RunnableFuture继承了Runnable与Future接口,没什么好说的。

    以上说的,Runnable、Callable、Future以及RunnableFuture都是接口,真正干活还得靠FutureTask


    FutureTask

    FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable与Future接口,因此FutureTask既可以作为一个Runnable被Thread执行,也可以获取到Future异步计算的结果。

    从上面的类图可以看到,传入一个Callable类型的参数可以构造FutureTask对象。

    下面这个例子,使用构造出来的FutureTask对象,传入Thread的构造函数中,调用start方法以启动线程。

    package com.qcy.testFutureTask;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    /**
     * @author qcy
     * @create 2020/09/07 09:27:06
     */
    public class Main {
    
        static class Task implements Callable<Integer> {
            @Override
            public Integer call() throws Exception {
                //模拟任务耗时
                Thread.sleep(2000);
                return 1;
            }
        }
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //FutureTask接收一个Callable实现类
            FutureTask<Integer> futureTask = new FutureTask<>(new Task());
    
            //FutureTask实现了RunnableFuture,间接实现了Runnable接口,因此可作为参数传给Thread的构造方法中
            new Thread(futureTask).start();
    
            //get()方法会阻塞2秒,输出1
            int result = futureTask.get();
            System.out.println(result);
        }
    }
    

    当然也可以将构造出来FutureTask对象,提交进线程池中。对线程池不了解的同学,可以移步这一篇文章说说线程池

    package com.qcy.testFutureTask;
    
    import java.util.concurrent.*;
    
    /**
     * @author qcy
     * @create 2020/09/07 09:27:06
     */
    public class Main {
    
        static class Task implements Callable<Integer> {
            @Override
            public Integer call() throws Exception {
                //模拟任务耗时
                Thread.sleep(2000);
                return 1;
            }
        }
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ExecutorService pool = Executors.newFixedThreadPool(1);
    
            //FutureTask接收一个Callable实现类
            FutureTask<Integer> futureTask = new FutureTask<>(new Task());
    
            //这里使用一个Runnable的实现类,因此可以使用FutureTask
            pool.submit(futureTask);
    
            //get()方法会阻塞2秒,输出1
            int result = futureTask.get();
            System.out.println(result);
    
            pool.shutdown();
        }
    }
    

    当然,ExecutorService内的submit方法,可以直接传入Callable的实现类。

    package com.qcy.testFutureTask;
    
    import java.util.concurrent.*;
    
    /**
     * @author qcy
     * @create 2020/09/07 09:27:06
     */
    public class Main {
    
        static class Task implements Callable<Integer> {
            @Override
            public Integer call() throws Exception {
                //模拟任务耗时
                Thread.sleep(2000);
                return 1;
            }
        }
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ExecutorService pool = Executors.newFixedThreadPool(1);
    
            //这里使用一个Callable的实现类,因此可以使用FutureTask
            Future<Integer> future = pool.submit(new Task());
    
            //get()方法会阻塞2秒,输出1
            int result = future.get();
            System.out.println(result);
    
            pool.shutdown();
        }
    }
    

    ?

    cs