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