Java Callable和Future
在本文中,我们将看到并发API的两个有趣的功能,Callable和Future。
Java中的Callable
考虑一个计算量很大的场景,我们想将其拆分为多个子任务,这些子任务由多个线程执行,每个线程都在一部分任务上工作。一旦所有线程完成其任务,就可以合并部分结果以获取计算结果。
使用Runnable设计这种情况会很困难,因为Runnable不会返回结果。该缺陷由Java中的Callable填补,因为它可以返回结果并可能引发异常。
可回收接口
Java中的可调用接口具有单个方法调用,该方法可以计算结果并返回结果,或者在无法执行结果时引发异常。
public interface Callable<V> { V call() throws Exception; }
因此,我们需要实现call()方法以提供必须由线程实现的任务作为异步计算。这是Callable实现的一个简单示例
Callable<String> callable = new Callable<String>() { public String call() { return "Value returned from Callable"; } };
由于Callable是功能性接口,因此也可以将其实现为lambda表达式。
Callable<String> callable = ()->{ return "Value returned from Callable"; };
使用ExecutorService运行可调用任务
要执行Callable,使用ExecutorService的Submit()方法。
<T> Future <T> Submit(Callable <T> task)–提交可调用任务,该任务返回要执行的值,并返回表示任务的未决结果的Future。
提交可调用任务时,将在其自己的线程中异步执行该任务。目前尚不知道异步计算的结果何时可用,我们所知道的是它将在将来使用。因此,适当命名的接口Future表示可调用任务的返回值。
Java Callable and Future示例
这是一个简单的示例,显示了如何使用ExecutorService提交可调用任务以及如何使用Future获得返回值。
public class CallableDemo { public static void main(String[] args) { ExecutorService executor = Executors.newSingleThreadExecutor(); Date date = new Date(); System.out.println("Submitting callable task " + date); // submitting callable task Future<String> future = executor.submit(()->{ TimeUnit.SECONDS.sleep(4); return "Value returned from Callable"; }); System.out.println("Submitted callable task " + new Date()); // getting result try { System.out.println("Returned value-" + future.get() + " at " + new Date()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } executor.shutdown(); } }
输出:
Submitting callable task Tue Dec 04 11:18:05 IST 2018 Submitted callable task Tue Dec 04 11:18:05 IST 2018 Returned value-Value returned from Callable at Tue Dec 04 11:18:09 IST 2018
如我们所见,可调用任务被提交执行以在其自己的线程中执行,而主线程继续执行(第二个System.out在可调用提交后立即执行)。
然后,调用get方法以检索计算结果,因为get()是阻塞调用,因此如果需要,它将等待计算完成。
Java的Future
Future代表异步计算的结果。
Future接口提供了一些方法来检查计算是否完成,等待其完成以及检索计算结果。
cancel(bollean interruptFlag)–尝试取消执行此任务。
get()–必要时等待计算完成,然后检索其结果。
get(long timeout,TimeUnit unit)–必要时最多等待给定时间以完成计算,然后检索其结果(如果有)。
isCancelled()–如果此任务在正常完成之前被取消,则返回true。
isDone()–如果此任务完成,则返回true。
Callable 和 Future 示例
这是一个可调用和将来的示例,其中使用两个线程的池执行4个可调用任务。
public class CallableDemo { public static void main(String[] args) { // Pool of 2 threads ExecutorService executor = Executors.newFixedThreadPool(2); System.out.println("Submitting callable tasks " + new Date()); Future<String> f1 = executor.submit(new MyCallable("Callable task-1")); Future<String> f2 = executor.submit(new MyCallable("Callable task-2")); Future<String> f3 = executor.submit(new MyCallable("Callable task-3")); Future<String> f4 = executor.submit(new MyCallable("Callable task-4")); System.out.println("Submitted callable task " + new Date()); // getting result try { // Calling get() method to get the future value System.out.println("Value for task-1 " + f1.get() + " at " + new Date()); System.out.println("Value for task-2 " + f2.get() + " at " + new Date()); while(!f3.isDone()) { System.out.println("Waiting for task-3 to complete " + f2.get()); TimeUnit.MILLISECONDS.sleep(500); } System.out.println("Value for task-3 after it is completed " + f3.get() + " at " + new Date()); System.out.println("Value for task-4 " + f4.get() + " at " + new Date()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } executor.shutdown(); } } class MyCallable implements Callable<String> { String str; MyCallable(String str){ this.str = str; } @Override public String call() throws Exception { System.out.println("In call method, thread name- " + Thread.currentThread().getName()); TimeUnit.SECONDS.sleep(2); return str; } }
输出:
Submitting callable tasks Tue Dec 04 11:47:23 IST 2018 Submitted callable task Tue Dec 04 11:47:23 IST 2018 In call method, thread name- pool-1-thread-1 In call method, thread name- pool-1-thread-2 In call method, thread name- pool-1-thread-2 In call method, thread name- pool-1-thread-1 Value for task-1 Callable task-1 at Tue Dec 04 11:47:25 IST 2018 Value for task-2 Callable task-2 at Tue Dec 04 11:47:25 IST 2018 Waiting for task-3 to complete Callable task-2 Waiting for task-3 to complete Callable task-2 Waiting for task-3 to complete Callable task-2 Waiting for task-3 to complete Callable task-2 Value for task-3 after it is completed Callable task-3 at Tue Dec 04 11:47:27 IST 2018 Value for task-4 Callable task-4 at Tue Dec 04 11:47:27 IST 2018
从输出中可以看到,线程池中的一个线程执行了两个可调用任务,而另一个线程执行了两个任务。在示例中,isDone()方法还用于检查提交的任务是否完成。