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