咱们在jdk1.8之前,都是用FutureTask的get办法来获取异步执行的后果。
在演示之前,先贴一下共用的代码。
ConcurrentSupport:
public class ConcurrentSupport { public static String processOne() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return getNow() + "#one"; } public static String processTwo() { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } return getNow() + "#two"; } public static String processThree() { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } return getNow() + "#three"; } public static String getNow() { return LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")); }}
TaskA:
class TaskA implements Callable { @Override public Object call() throws Exception { return ConcurrentSupport.processOne(); }}
TaskB:
class TaskB implements Callable { @Override public Object call() throws Exception { return ConcurrentSupport.processTwo(); }}
TaskC:
class TaskC implements Callable { @Override public Object call() throws Exception { return ConcurrentSupport.processThree(); }}
一般状况
比方有3个工作,别离耗时1s、2s、3s,在同步执行的时候,整个耗时就是6s。
public class NormalDemo { public static void main(String[] args) { long start = System.currentTimeMillis(); System.out.println(ConcurrentSupport.processOne()); System.out.println(ConcurrentSupport.processTwo()); System.out.println(ConcurrentSupport.processThree()); System.out.println("耗时:" + (System.currentTimeMillis() - start)); }}
FutureTask
用FutureTask的状况,就是取最长工夫的那个,所以最终工夫是3s。
public class FutureTaskDemo { public static void main(String[] args) throws ExecutionException, InterruptedException { // 创立线程池 ExecutorService executor = Executors.newFixedThreadPool(3); long start = System.currentTimeMillis(); // 创立FutureTask FutureTask taskA = new FutureTask(new TaskA()); FutureTask taskB = new FutureTask(new TaskB()); FutureTask taskC = new FutureTask(new TaskC()); executor.submit(taskA); executor.submit(taskB); executor.submit(taskC); System.out.println(taskC.get()); System.out.println(taskA.get()); System.out.println(taskB.get()); System.out.println("耗时:" + (System.currentTimeMillis() - start)); }}
CompletionService
然而FutureTask也有一个小瑕疵,比方下面的TaskC执行的工夫最长,间接把TaskA和TaskB的打印工作给阻塞了,打印的后果是three、one、two。
有木有方法是哪个工作先执行胜利,就先打印(或者对这个后果其余解决)这个后果呢?
CompletionService做的就是这个事件。从后果能够看出打印one、two、three。
public class CompletionServiceDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { // 创立线程池 ExecutorService executor = Executors.newFixedThreadPool(3); long start = System.currentTimeMillis(); // 创立CompletionService CompletionService<String> cs = new ExecutorCompletionService<>(executor); // 用于保留Future对象 List<Future<String>> futures = new ArrayList<>(3); // 提交FutureTask futures.add(cs.submit(new TaskC())); futures.add(cs.submit(new TaskA())); futures.add(cs.submit(new TaskB())); for (int i = 0; i < 3; i++) { String result = cs.take().get(); System.out.println(result); } System.out.println("耗时:" + (System.currentTimeMillis() - start)); }}
CompletableFuture
如果有一个工作是这样的,A1执行完执行A2,B1执行完执行B2,A2和B2执行完,再执行C。
JDK1.8提供了CompletableFuture这个优雅的解决方案。
比方上面的例子,就是f1和f2执行完后,才执行f3。
CompletableFuture的办法中,runAsync是没有返回值的,supplyAsync是有返回值的。
public class CompletableFutureDemo { public static void main(String[] args) { CompletableFuture<Void> f1 = CompletableFuture.runAsync(() -> { String one = ConcurrentSupport.processOne(); System.out.println(one); } ); CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> { String two = ConcurrentSupport.processTwo(); System.out.println(two); return two; } ); CompletableFuture<String> f3 = f1.thenCombine(f2, (__, tf) -> { System.out.println("f3#" + tf); return "f3"; } ); f3.join(); }}