一. CompletableFuturejava
1.Future接口网络
Future设计的初衷:对未来某个时刻会发生的结果进行建模。多线程
它建模了一种异步计算,返回一个执行运算结果的引用,当运算结束后,这个引用被返回给调用方。在Future中出发那些潜在耗时的操做把调用线程解放出来,让它能继续执行其余有价值的工做,再也不须要等待耗时的操做完成。异步
Future的优势:比更底层的Thread更易用。ide
要使用Future,一般只须要将耗时的操做封装在一个Callable对象中,再将它提交给ExecutorService。函数
ExecutorService executor = Executors.newCachedThreadPool(); Future<Double> future = executor.submit(new Callable<Double>() { //向ExecutorService提交一个Callable对象 @Override public Double call() throws Exception { return doSomeLongComputation(); //以异步方式在新的线程中执行耗时的操做 } }); doSomethingElse(); //异步操做进行的同时,你能够作其余的事情 try { //获取异步操做的结果,若是最终被阻塞,没法获得结果,那么在最多等待1秒钟以后退出 Double result = future.get(1, TimeUnit.SECONDS); } catch (ExecutionException ee) { //加上抛出一个异常 } catch (InterruptedException ie) { //当前线程在等待过程当中被中断 } catch (TimeoutException te) { //在Future对象完成以前超过已过时 }
2. 实现异步API、代码避免阻塞spa
使用工厂方法supplyAsync建立CompletableFuture线程
public Future<Double> getPriceAsync2(String product) { return CompletableFuture.supplyAsync(() -> calculatePrice(product)); }
supplyAsync方法接受一个生产者(Supplier)做为参数,返回一个CompletableFuture对象,该对象完成异步执行后会读取调用生产者方法的返回值。设计
生产者方法会交由ForkJoinPool池中的某个执行线程(Executor)运行,可是你也可使用supplyAsync方法的重载版本,传递第二个参数指定不一样的执行线程执行生产者方法。对象
join方法等待异步操做结束
CompletableFuture类中的join方法和Future接口中的get有相同的含义,等待运行结束。而且也声明在Future接口中,惟一的不一样是join方法不会抛出任何检测到的异常。所以使用它时不须要再使用try/catch语句块。
public List<String> findPrices(String product) { //使用CompletableFuture以异步方式计算每种商品的价格 List<CompletableFuture<String>> priceFutures = shops.stream() .map(shop -> CompletableFuture.supplyAsync(() -> shop.getName() + " price is " + shop.getPriceAsync(product))) .collect(Collectors.toList()); //等待全部异步操做结束 return priceFutures.stream(). map(CompletableFuture::join) .collect(Collectors.toList()); }
使用定制执行器
建立一个配有线程池的执行器。
线程数的选择:N(threads) = N(CPU) * U(CPU) * (1 + W/C)
-- N(CPU):处理器的核的数目,能够经过Runtime.getRuntime().availableProcessors()获得;
-- U(CPU):指望的CPU利用率(该值应该介于0和1之间);
-- W/C:等待时间与计算时间的比率。
//建立一个线程池,线程池中线程的数目为100和商店数目两者中较小的一个值(这里100为线程池的上限) private final Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100), new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setDaemon(true); //使用守护线程--这种方式不会阻止程序的关停 return t; } });
集合进行并行计算有两种方式:并行流和CompletableFutures。
-- 计算密集型操做,而且没有I/O,推荐使用Stream接口。由于实现简单,同时效率也多是最高的(若是全部的线程都是计算密集型的,那就没有必要建立比处理器核数更多的线程);
-- 若是并行的工做单元还涉及等待I/O的操做(包括网络链接等待),那么使用CompletableFuture灵活性更好。这种状况下处理流的流水线中若是发生I/O等待,流的延迟特性会让咱们很难判断到底何时触发了等待。
3. 对多个异步任务进行流水线操做
thenApply:将一个由字符串转换Quote的方法做为参数传递给他
thenCompose:该方法容许你对两个异步操做进行流水线,第一个操做完成时,将其结果做为参数传递给第二个操做。
对第一个CompletableFuture对象调用thenCompose,并向其传递一个函数。当第一个CompletableFuture执行完毕后,他的结果将做为该函数的参数,这个函数的返回值是以第一个CompletableFuture的返回作输入计算出的第二 个CompletableFuture对象。
使用thenCompose减小不少线程切换开销。
thenCombine:将两个CompletableFuture对象结果整合起来。该方法接收名为BiFunction的第二参数,这个参数定义了两个CompletableFuture对象完成计算后,如何合并。
thenAccept:方法接收CompletableFuture执行完毕后的返回值作参数。没必要等待那些还未返回的结果。