Callable是可以获取返回值的Runnable,不过Callable只能通过线程池,来直接的提交任务。

如果通过Runnable来执行任务,则只能通过FutureTask来获取返回值。

线程池ExecutoerService的execute()方法,只接收Runnable入参。要想获取任务返回值,只能通过FutureTask。

submit()方法,可以接收Runnable和Callable入参。获取返回值,既可以通过Future+Callable,也可以通过FutureTask+Runnable/Callable。

获取任务返回值,从结果上来看,Runnable任务和Callable任务都能办得到;但是从思路上来看,还是应该直接使用Callable任务!

/**
 * 线程池工具类:
 * Executors
 *
 * 线程池相关:
 * Executor, ExecutorService,
 *
 * 线程池任务提交方法:
 * execute(), submit()
 *
 * 以及:
 * Runnable, Callabel
 *
 * 和回调相关:
 * Future, FutureTask
 */
@Slf4j
public class MyExecutor {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        ExecutorService pool = Executors.newSingleThreadExecutor(); // 单线程的线程池,也就是在另一个线程里串行
        ExecutorService pool = Executors.newFixedThreadPool(2); // 固定2个线程的线程池

        pool.execute(new Runnable() {
            @Override
            public void run() {
                log.info("运行到 直接-Runnable 里面了:Execute");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) { // Runnable里面的Exception必须要try/catch掉!
                    e.printStackTrace();
                }
            }
        }); // 这样提交任务,是没有办法获取到任务的返回值的。就像下面的wrapper2。

        // 断进去发现,submit方法,实际上就是先把入参Runnable/Callable,封装成FutureTaks,然后执行execute
        pool.submit(new Runnable() {
            @Override
            public void run() {
                log.info("运行到 直接-Runnable 里面了:Submit");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) { // Runnable里面的Exception必须要try/catch掉!
                    e.printStackTrace();
                }
            }
        }); // submit() + Runnable,只能通过FutureTask来获取任务的返回值。可能是为了兼容,才会出现这种情况吧!

        Future<String> future = pool.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                log.info("运行到 直接-Callable 里面了");
                Thread.sleep(3000);
                return "hello Callable!";
            }
        });
        String ret = future.get();
        log.info("Callable submit()到线程池的运行的结果:{}", ret);

        FutureTask<String> futureTask1 = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                log.info("运行到 FutureTask-Callable 里面了");
                Thread.sleep(3000);
                return "FutureTask + Callable";
            }
        });
        pool.submit(futureTask1);
        ret = futureTask1.get();
        log.info("FutureTask+Callable+submit()的运行的结果:{}", ret);

        pool.execute(futureTask1);
        ret = futureTask1.get();
        log.info("FutureTask+Callable+execute()的运行的结果:{}", ret);

        ResultWrapper wrapper1 = new ResultWrapper();
        ResultWrapper wrapper2 = new ResultWrapper();
        FutureTask<ResultWrapper> futureTask2 = new FutureTask<>(new Runnable() {
            @Override
            public void run() {
                log.info("运行到 FutureTask-Runnable 里面了");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                wrapper1.setResult("FutureTask + Runnable");
                wrapper2.setResult("FutureTask + Runnable"); // 开始整活
            }
        }, wrapper1); // 注意这里的入参是wrapper1
        pool.execute(futureTask2);
        // 这里直接运行这个,应该是会获取一个null出来的。
        // 从结果上看,是因为任务提交到线程池后,先执行了sleep,还没有执行到set方法。
        log.info("线程池提交任务后,立刻尝试通过wrapper1来获取result: {}", wrapper1.getResult());
        ResultWrapper ret2 = futureTask2.get();
        log.info("通过futureTask2来异步获取执行结果: {}", ret2.getResult());

        // 下面这两个wrapper,此刻虽然都能获取到结果,但是整个思路是乱七八糟的。
        // 真正的异步,不一定会像这个main函数,在一个函数内部执行,wrapper1/2,应该只是一个临时的引用,不会一直传递下去的。
        // 正确的思路,还是应该通过Future或者FutureTask,来异步的,阻塞的获取结果。
        log.info("异步获取到结果后,通过wrapper1来获取result: {}", wrapper1.getResult());
        log.info("异步获取到结果后,通过wrapper2来获取result: {}", wrapper2.getResult());

        // 总之,Runnable来获取任务执行结果,就是看起来乱糟糟的。应该尽量避免!
        pool.shutdown();
    }

    private static class ResultWrapper{
        @Getter
        @Setter
        private String result;
    }
}
2022-02-13 14:35:15 [INFO] [pool-1-thread-1|cn.line.MyExecutor:34] 运行到 直接-Runnable 里面了:Execute
 2022-02-13 14:35:15 [INFO] [pool-1-thread-2|cn.line.MyExecutor:47] 运行到 直接-Runnable 里面了:Submit
 2022-02-13 14:35:18 [INFO] [pool-1-thread-2|cn.line.MyExecutor:59] 运行到 直接-Callable 里面了
 2022-02-13 14:35:21 [INFO] [main|cn.line.MyExecutor:65] Callable submit()到线程池的运行的结果:hello Callable!
 2022-02-13 14:35:21 [INFO] [pool-1-thread-1|cn.line.MyExecutor:70] 运行到 FutureTask-Callable 里面了
 2022-02-13 14:35:24 [INFO] [main|cn.line.MyExecutor:77] FutureTask+Callable+submit()的运行的结果:FutureTask + Callable
 2022-02-13 14:35:24 [INFO] [main|cn.line.MyExecutor:81] FutureTask+Callable+execute()的运行的结果:FutureTask + Callable
 2022-02-13 14:35:24 [INFO] [main|cn.line.MyExecutor:101] 线程池提交任务后,立刻尝试通过wrapper1来获取result: null
 2022-02-13 14:35:24 [INFO] [pool-1-thread-1|cn.line.MyExecutor:88] 运行到 FutureTask-Runnable 里面了
 2022-02-13 14:35:27 [INFO] [main|cn.line.MyExecutor:103] 通过futureTask2来异步获取执行结果: FutureTask + Runnable
 2022-02-13 14:35:27 [INFO] [main|cn.line.MyExecutor:108] 异步获取到结果后,通过wrapper1来获取result: FutureTask + Runnable
 2022-02-13 14:35:27 [INFO] [main|cn.line.MyExecutor:109] 异步获取到结果后,通过wrapper2来获取result: FutureTask + Runnable