Open dccmmtop opened 2 years ago
无论是继承 Thread 类,还是实现 Runnale 接口,或者使用线程池的 execute 方法去执行一个异步任务,都无法将这个任务的返回值带出来,以 Runnale 接口为例:
/** * @since JDK1.0 */ @FunctionalInterface public interface Runnable { public abstract void run(); }
可以看到 run() 方法没有参数,没有返回值,没有抛出异常,因此不能适用于那些需要异步任务返回值得场景,由此就诞生了可以获取异步任务结果的工具: Callable
又是 Doug Lea 大师
/** * @see Executor * @since 1.5 * @author Doug Lea * @param <V> the result type of method {@code call} */ @FunctionalInterface public interface Callable<V> { V call() throws Exception; }
Callable 是一个泛型接口,里面只有一个方法,call(), 可以返回泛型值V, Cacheable 与 Runnable 接口很是相似,下面看一下他们之间的区别
两个接口都是用于多线程执行任务的,但是他们的使用场景的差别还是很大的。
Runnable 可以用在Thread类中,也可以在 ExecutorService 中配合线程池使用,但是 Callable 只能用于 ExecutorService 中,Thread 类中没有 Cacheable 的身影:
Runnable 接口中的 run 方法签名上没有 throws, 自然也不能向上传播受检异常。而 Callable 接口中的 call f方法可以向上抛出异常。
区别总结如下:
上面说过,Callable 接口只能在 ExecutorService 中使用,下面看一下如何使用的
可以看到在 submit 方法中可以接收一个实现了 Callable 接口的任务, 返回的是 Future 类型。而 execute 方法无返回值。 那么 Future 是什么呢? 如何通过 Future 拿到返回值呢?
Future 是一个接口,通过方法名就可以看出他们的用途:
// 取消任务 boolean cancel(boolean mayInterruptIfRunning); // 获取任务执行结果 V get() throws InterruptedException, ExecutionException; // 获取任务执行结果,带有超时时间限制 V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; // 判断任务是否已经取消 boolean isCancelled(); // 判断任务是否已经结束 boolean isDone();
看一下如何使用:
public class FutureDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newSingleThreadExecutor(); // 使用 Callable ,可以获取返回值 Callable<String> callable = () -> { System.out.println("进入 Callable 的 call 方法"); // 模拟子线程任务,在此睡眠 2s, // 小细节:由于 call 方法会抛出 Exception,这里不用像使用 Runnable 的run 方法那样 try/catch 了 Thread.sleep(5000); return "Hello from Callable"; }; System.out.println("提交 Callable 到线程池"); Future<String> future = executorService.submit(callable); System.out.println("主线程继续执行"); System.out.println("主线程等待获取 Future 结果"); // //检查任务是否做完 // while(!future.isDone()) { // System.out.println("Task is still not done..."); // Thread.sleep(1000); // } // Future.get() blocks until the result is available String result = future.get(); System.out.println("主线程获取到 Future 结果: " + result); executorService.shutdown(); } }
结果如下:
提交 Callable 到线程池 主线程继续执行 主线程等待获取 Future 结果 进入 Callable 的 call 方法 主线程获取到 Future 结果: Hello from Callable Process finished with exit code 0
如果子程序运行时间过长,或者其他原因,我们想 cancel 子程序的运行,则我们可以使用 Future 提供的 cancel 方法,继续对程序做一些修改
while(!future.isDone()) { System.out.println("子线程任务还没有结束..."); Thread.sleep(1000); double elapsedTimeInSec = (System.nanoTime() - startTime)/1000000000.0; // 如果程序运行时间大于 1s,则取消子线程的运行 if(elapsedTimeInSec > 1) { future.cancel(true); } }
特别注意的是,如果使用 cancel 方法取消了任务, get() 方法会抛出一个 CancellationException 异常。
到这里已经知道和配合使用 Cacheable 和 Future 来获取异步任务的返回值了。总结就只有3点:
刚刚我们看 ExecutorService 中的 submit 方法返回的是 Future 接口,然后通过接口中的get 方法获取任务的返回值,其实 submit 返回的是 Future 的实现类: FutureTask, 至于 FutureTask 是如何配合线程池拿到任务的返回值,就需要深入源码查看底层实现了,这个以后再新写一遍博客讲解。
http://www.lllpan.top/article/102
为什么需要 Callable
无论是继承 Thread 类,还是实现 Runnale 接口,或者使用线程池的 execute 方法去执行一个异步任务,都无法将这个任务的返回值带出来,以 Runnale 接口为例:
可以看到 run() 方法没有参数,没有返回值,没有抛出异常,因此不能适用于那些需要异步任务返回值得场景,由此就诞生了可以获取异步任务结果的工具: Callable
Callable
又是 Doug Lea 大师
Callable 是一个泛型接口,里面只有一个方法,call(), 可以返回泛型值V, Cacheable 与 Runnable 接口很是相似,下面看一下他们之间的区别
Callable VS Runnable
两个接口都是用于多线程执行任务的,但是他们的使用场景的差别还是很大的。
执行机制上的差别
Runnable 可以用在Thread类中,也可以在 ExecutorService 中配合线程池使用,但是 Callable 只能用于 ExecutorService 中,Thread 类中没有 Cacheable 的身影:
异常处理的差别
Runnable 接口中的 run 方法签名上没有 throws, 自然也不能向上传播受检异常。而 Callable 接口中的 call f方法可以向上抛出异常。
区别总结如下:
Callable 在线程池中的应用
上面说过,Callable 接口只能在 ExecutorService 中使用,下面看一下如何使用的
可以看到在 submit 方法中可以接收一个实现了 Callable 接口的任务, 返回的是 Future 类型。而 execute 方法无返回值。 那么 Future 是什么呢? 如何通过 Future 拿到返回值呢?
Future
Future 是一个接口,通过方法名就可以看出他们的用途:
看一下如何使用:
结果如下:
如果子程序运行时间过长,或者其他原因,我们想 cancel 子程序的运行,则我们可以使用 Future 提供的 cancel 方法,继续对程序做一些修改
特别注意的是,如果使用 cancel 方法取消了任务, get() 方法会抛出一个 CancellationException 异常。
到这里已经知道和配合使用 Cacheable 和 Future 来获取异步任务的返回值了。总结就只有3点:
刚刚我们看 ExecutorService 中的 submit 方法返回的是 Future 接口,然后通过接口中的get 方法获取任务的返回值,其实 submit 返回的是 Future 的实现类: FutureTask, 至于 FutureTask 是如何配合线程池拿到任务的返回值,就需要深入源码查看底层实现了,这个以后再新写一遍博客讲解。
参考
http://www.lllpan.top/article/102