科普网站栏目建设方案网站 建站模式
科普网站栏目建设方案,网站 建站模式,运用asp做购物网站的心得,建网站选服务器一个示例回顾Future一些业务场景我们需要使用多线程异步执行任务#xff0c;加快任务执行速度。JDK5新增了Future接口#xff0c;用于描述一个异步计算的结果。虽然 Future 以及相关使用方法提供了异步执行任务的能力#xff0c;但是对于结果的获取却是很不方便#xff0c;…一个示例回顾Future一些业务场景我们需要使用多线程异步执行任务加快任务执行速度。JDK5新增了Future接口用于描述一个异步计算的结果。虽然 Future 以及相关使用方法提供了异步执行任务的能力但是对于结果的获取却是很不方便我们必须使用Future.get()的方式阻塞调用线程或者使用轮询方式判断Future.isDone任务是否结束再获取结果。这两种处理方式都不是很优雅相关代码如下:Test public void testFuture() throws ExecutionException, InterruptedException { ExecutorService executorService Executors.newFixedThreadPool(5); FutureString future executorService.submit(() - { Thread.sleep(2000); return hello; }); System.out.println(future.get()); System.out.println(end); }与此同时Future无法解决多个异步任务需要相互依赖的场景简单点说就是主线程需要等待子线程任务执行完毕之后在进行执行这个时候你可能想到了「CountDownLatch」没错确实可以解决代码如下。这里定义两个Future第一个通过用户id获取用户信息第二个通过商品id获取商品信息。Test public void testCountDownLatch() throws InterruptedException, ExecutionException { ExecutorService executorService Executors.newFixedThreadPool(5); CountDownLatch downLatch new CountDownLatch(2); long startTime System.currentTimeMillis(); FutureString userFuture executorService.submit(() - { //模拟查询商品耗时500毫秒 Thread.sleep(500); downLatch.countDown(); return 用户A; }); FutureString goodsFuture executorService.submit(() - { //模拟查询商品耗时500毫秒 Thread.sleep(400); downLatch.countDown(); return 商品A; }); downLatch.await(); //模拟主程序耗时时间 Thread.sleep(600); System.out.println(获取用户信息: userFuture.get()); System.out.println(获取商品信息: goodsFuture.get()); System.out.println(总共用时 (System.currentTimeMillis() - startTime) ms); }「运行结果」获取用户信息:用户A 获取商品信息:商品A 总共用时1110ms从运行结果可以看出结果都已经获取而且如果我们不用异步操作执行时间应该是:500400600 1500,用异步操作后实际只用1110。但是Java8以后我不在认为这是一种优雅的解决方式接下来来了解下CompletableFuture的使用。通过CompletableFuture实现上面示例Test public void testCompletableInfo() throws InterruptedException, ExecutionException { long startTime System.currentTimeMillis(); //调用用户服务获取用户基本信息 CompletableFutureString userFuture CompletableFuture.supplyAsync(() - //模拟查询商品耗时500毫秒 { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } return 用户A; }); //调用商品服务获取商品基本信息 CompletableFutureString goodsFuture CompletableFuture.supplyAsync(() - //模拟查询商品耗时500毫秒 { try { Thread.sleep(400); } catch (InterruptedException e) { e.printStackTrace(); } return 商品A; }); System.out.println(获取用户信息: userFuture.get()); System.out.println(获取商品信息: goodsFuture.get()); //模拟主程序耗时时间 Thread.sleep(600); System.out.println(总共用时 (System.currentTimeMillis() - startTime) ms); }运行结果获取用户信息:用户A 获取商品信息:商品A 总共用时1112ms通过CompletableFuture可以很轻松的实现CountDownLatch的功能你以为这就结束了远远不止CompletableFuture比这要强多了。比如可以实现:任务1执行完了再执行任务2,甚至任务1执行的结果作为任务2的入参数等等强大功能下面就来学学CompletableFuture的API。CompletableFuture创建方式1、常用的4种创建方式CompletableFuture源码中有四个静态方法用来执行异步任务public static U CompletableFutureU supplyAsync(SupplierU supplier){..} public static U CompletableFutureU supplyAsync(SupplierU supplier,Executor executor){..} public static CompletableFutureVoid runAsync(Runnable runnable){..} public static CompletableFutureVoid runAsync(Runnable runnable,Executor executor){..}一般我们用上面的静态方法来创建CompletableFuture这里也解释下他们的区别:「supplyAsync」执行任务支持返回值。「runAsync」执行任务没有返回值。「supplyAsync方法」//使用默认内置线程池ForkJoinPool.commonPool()根据supplier构建执行任务 public static U CompletableFutureU supplyAsync(SupplierU supplier) //自定义线程根据supplier构建执行任务 public static U CompletableFutureU supplyAsync(SupplierU supplier, Executor executor)「runAsync方法」//使用默认内置线程池ForkJoinPool.commonPool()根据runnable构建执行任务 public static CompletableFutureVoid runAsync(Runnable runnable) //自定义线程根据runnable构建执行任务 public static CompletableFutureVoid runAsync(Runnable runnable, Executor executor)2、结果获取的4种方式对于结果的获取CompltableFuture类提供了四种方式//方式一 public T get() //方式二 public T get(long timeout, TimeUnit unit) //方式三 public T getNow(T valueIfAbsent) //方式四 public T join()说明「get()和get(long timeout, TimeUnit unit)」 在Future中就已经提供了后者提供超时处理如果在指定时间内未获取结果将抛出超时异常「getNow」 立即获取结果不阻塞结果计算已完成将返回结果或计算过程中的异常如果未计算完成将返回设定的valueIfAbsent值「join」 方法里不会抛出异常示例Test public void testCompletableGet() throws InterruptedException, ExecutionException { CompletableFutureString cp1 CompletableFuture.supplyAsync(() - { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return 商品A; }); // getNow方法测试 System.out.println(cp1.getNow(商品B)); //join方法测试 CompletableFutureInteger cp2 CompletableFuture.supplyAsync((() - 1 / 0)); System.out.println(cp2.join()); System.out.println(-----------------------------------------------------); //get方法测试 CompletableFutureInteger cp3 CompletableFuture.supplyAsync((() - 1 / 0)); System.out.println(cp3.get()); }「运行结果」第一个执行结果为「商品B」因为要先睡上1秒结果不能立即获取join方法获取结果方法里不会抛异常但是执行结果会抛异常抛出的异常为CompletionExceptionget方法获取结果方法里将抛出异常执行结果抛出的异常为ExecutionException异步回调方法1、thenRun/thenRunAsync通俗点讲就是「做完第一个任务后再做第二个任务,第二个任务也没有返回值」。示例Test public void testCompletableThenRunAsync() throws InterruptedException, ExecutionException { long startTime System.currentTimeMillis(); CompletableFutureVoid cp1 CompletableFuture.runAsync(() - { try { //执行任务A Thread.sleep(600); } catch (InterruptedException e) { e.printStackTrace(); } }); CompletableFutureVoid cp2 cp1.thenRun(() - { try { //执行任务B Thread.sleep(400); } catch (InterruptedException e) { e.printStackTrace(); } }); // get方法测试 System.out.println(cp2.get()); //模拟主程序耗时时间 Thread.sleep(600); System.out.println(总共用时 (System.currentTimeMillis() - startTime) ms); } //运行结果 /** * null * 总共用时1610ms */「thenRun 和thenRunAsync有什么区别呢」如果你执行第一个任务的时候传入了一个自定义线程池调用thenRun方法执行第二个任务时则第二个任务和第一个任务是共用同一个线程池。调用thenRunAsync执行第二个任务时则第一个任务使用的是你自己传入的线程池第二个任务使用的是ForkJoin线程池。说明: 后面介绍的thenAccept和thenAcceptAsyncthenApply和thenApplyAsync等它们之间的区别也是这个。2、thenAccept/thenAcceptAsync第一个任务执行完成后执行第二个回调方法任务会将该任务的执行结果作为入参传递到回调方法中但是回调方法是没有返回值的。示例Test public void testCompletableThenAccept() throws ExecutionException, InterruptedException { long startTime System.currentTimeMillis(); CompletableFutureString cp1 CompletableFuture.supplyAsync(() - { return dev; }); CompletableFutureVoid cp2 cp1.thenAccept((a) - { System.out.println(上一个任务的返回结果为: a); }); cp2.get(); }3、 thenApply/thenApplyAsync表示第一个任务执行完成后执行第二个回调方法任务会将该任务的执行结果作为入参传递到回调方法中并且回调方法是有返回值的。示例Test public void testCompletableThenApply() throws ExecutionException, InterruptedException { CompletableFutureString cp1 CompletableFuture.supplyAsync(() - { return dev; }).thenApply((a) - { if(Objects.equals(a,dev)){ return dev; } return prod; }); System.out.println(当前环境为: cp1.get()); //输出: 当前环境为:dev }异常回调当CompletableFuture的任务不论是正常完成还是出现异常它都会调用「whenComplete」这回调函数。「正常完成」whenComplete返回结果和上级任务一致异常为null「出现异常」whenComplete返回结果为null异常为上级任务的异常即调用get()时正常完成时就获取到结果出现异常时就会抛出异常需要你处理该异常。下面来看看示例1、只用whenCompleteTest public void testCompletableWhenComplete() throws ExecutionException, InterruptedException { CompletableFutureDouble future CompletableFuture.supplyAsync(() - { if (Math.random() 0.5) { throw new RuntimeException(出错了); } System.out.println(正常结束); return 0.11; }).whenComplete((aDouble, throwable) - { if (aDouble null) { System.out.println(whenComplete aDouble is null); } else { System.out.println(whenComplete aDouble is aDouble); } if (throwable null) { System.out.println(whenComplete throwable is null); } else { System.out.println(whenComplete throwable is throwable.getMessage()); } }); System.out.println(最终返回的结果 future.get()); }正常完成没有异常时正常结束 whenComplete aDouble is 0.11 whenComplete throwable is null 最终返回的结果 0.11出现异常时get()会抛出异常whenComplete aDouble is null whenComplete throwable is java.lang.RuntimeException: 出错了 java.util.concurrent.ExecutionException: java.lang.RuntimeException: 出错了 at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357) at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)2、whenComplete exceptionally示例Test public void testWhenCompleteExceptionally() throws ExecutionException, InterruptedException { CompletableFutureDouble future CompletableFuture.supplyAsync(() - { if (Math.random() 0.5) { throw new RuntimeException(出错了); } System.out.println(正常结束); return 0.11; }).whenComplete((aDouble, throwable) - { if (aDouble null) { System.out.println(whenComplete aDouble is null); } else { System.out.println(whenComplete aDouble is aDouble); } if (throwable null) { System.out.println(whenComplete throwable is null); } else { System.out.println(whenComplete throwable is throwable.getMessage()); } }).exceptionally((throwable) - { System.out.println(exceptionally中异常 throwable.getMessage()); return 0.0; }); System.out.println(最终返回的结果 future.get()); }当出现异常时exceptionally中会捕获该异常给出默认返回值0.0。whenComplete aDouble is null whenComplete throwable is java.lang.RuntimeException: 出错了 exceptionally中异常java.lang.RuntimeException: 出错了 最终返回的结果 0.0多任务组合回调1、AND组合关系thenCombine / thenAcceptBoth / runAfterBoth都表示「当任务一和任务二都完成再执行任务三」。区别在于「runAfterBoth」不会把执行结果当做方法入参且没有返回值「thenAcceptBoth」: 会将两个任务的执行结果作为方法入参传递到指定方法中且无返回值「thenCombine」会将两个任务的执行结果作为方法入参传递到指定方法中且有返回值示例Test public void testCompletableThenCombine() throws ExecutionException, InterruptedException { //创建线程池 ExecutorService executorService Executors.newFixedThreadPool(10); //开启异步任务1 CompletableFutureInteger task CompletableFuture.supplyAsync(() - { System.out.println(异步任务1当前线程是 Thread.currentThread().getId()); int result 1 1; System.out.println(异步任务1结束); return result; }, executorService); //开启异步任务2 CompletableFutureInteger task2 CompletableFuture.supplyAsync(() - { System.out.println(异步任务2当前线程是 Thread.currentThread().getId()); int result 1 1; System.out.println(异步任务2结束); return result; }, executorService); //任务组合 CompletableFutureInteger task3 task.thenCombineAsync(task2, (f1, f2) - { System.out.println(执行任务3当前线程是 Thread.currentThread().getId()); System.out.println(任务1返回值 f1); System.out.println(任务2返回值 f2); return f1 f2; }, executorService); Integer res task3.get(); System.out.println(最终结果 res); }「运行结果」异步任务1当前线程是17 异步任务1结束 异步任务2当前线程是18 异步任务2结束 执行任务3当前线程是19 任务1返回值2 任务2返回值2 最终结果42、OR组合关系applyToEither / acceptEither / runAfterEither 都表示「两个任务只要有一个任务完成就执行任务三」。区别在于「runAfterEither」不会把执行结果当做方法入参且没有返回值「acceptEither」: 会将已经执行完成的任务作为方法入参传递到指定方法中且无返回值「applyToEither」会将已经执行完成的任务作为方法入参传递到指定方法中且有返回值示例Test public void testCompletableEitherAsync() { //创建线程池 ExecutorService executorService Executors.newFixedThreadPool(10); //开启异步任务1 CompletableFutureInteger task CompletableFuture.supplyAsync(() - { System.out.println(异步任务1当前线程是 Thread.currentThread().getId()); int result 1 1; System.out.println(异步任务1结束); return result; }, executorService); //开启异步任务2 CompletableFutureInteger task2 CompletableFuture.supplyAsync(() - { System.out.println(异步任务2当前线程是 Thread.currentThread().getId()); int result 1 2; try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(异步任务2结束); return result; }, executorService); //任务组合 task.acceptEitherAsync(task2, (res) - { System.out.println(执行任务3当前线程是 Thread.currentThread().getId()); System.out.println(上一个任务的结果为res); }, executorService); }运行结果//通过结果可以看出异步任务2都没有执行结束任务3获取的也是1的执行结果 异步任务1当前线程是17 异步任务1结束 异步任务2当前线程是18 执行任务3当前线程是19 上一个任务的结果为2注意如果把上面的核心线程数改为1也就是ExecutorService executorService Executors.newFixedThreadPool(1);运行结果就是下面的了会发现根本没有执行任务3显然是任务3直接被丢弃了。异步任务1当前线程是17 异步任务1结束 异步任务2当前线程是173、多任务组合「allOf」等待所有任务完成「anyOf」只要有一个任务完成示例allOf等待所有任务完成Test public void testCompletableAallOf() throws ExecutionException, InterruptedException { //创建线程池 ExecutorService executorService Executors.newFixedThreadPool(10); //开启异步任务1 CompletableFutureInteger task CompletableFuture.supplyAsync(() - { System.out.println(异步任务1当前线程是 Thread.currentThread().getId()); int result 1 1; System.out.println(异步任务1结束); return result; }, executorService); //开启异步任务2 CompletableFutureInteger task2 CompletableFuture.supplyAsync(() - { System.out.println(异步任务2当前线程是 Thread.currentThread().getId()); int result 1 2; try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(异步任务2结束); return result; }, executorService); //开启异步任务3 CompletableFutureInteger task3 CompletableFuture.supplyAsync(() - { System.out.println(异步任务3当前线程是 Thread.currentThread().getId()); int result 1 3; try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(异步任务3结束); return result; }, executorService); //任务组合 CompletableFutureVoid allOf CompletableFuture.allOf(task, task2, task3); //等待所有任务完成 allOf.get(); //获取任务的返回结果 System.out.println(task结果为 task.get()); System.out.println(task2结果为 task2.get()); System.out.println(task3结果为 task3.get()); }anyOf: 只要有一个任务完成Test public void testCompletableAnyOf() throws ExecutionException, InterruptedException { //创建线程池 ExecutorService executorService Executors.newFixedThreadPool(10); //开启异步任务1 CompletableFutureInteger task CompletableFuture.supplyAsync(() - { int result 1 1; return result; }, executorService); //开启异步任务2 CompletableFutureInteger task2 CompletableFuture.supplyAsync(() - { int result 1 2; return result; }, executorService); //开启异步任务3 CompletableFutureInteger task3 CompletableFuture.supplyAsync(() - { int result 1 3; return result; }, executorService); //任务组合 CompletableFutureObject anyOf CompletableFuture.anyOf(task, task2, task3); //只要有一个有任务完成 Object o anyOf.get(); System.out.println(完成的任务的结果 o); }CompletableFuture使用有哪些注意点CompletableFuture 使我们的异步编程更加便利的、代码更加优雅的同时我们也要关注下它使用的一些注意点。1、Future需要获取返回值才能获取异常信息Test public void testWhenCompleteExceptionally() { CompletableFutureDouble future CompletableFuture.supplyAsync(() - { if (1 1) { throw new RuntimeException(出错了); } return 0.11; }); //如果不加 get()方法这一行看不到异常信息 //future.get(); }Future需要获取返回值才能获取到异常信息。如果不加 get()/join()方法看不到异常信息。小伙伴们使用的时候注意一下哈,考虑是否加try...catch...或者使用exceptionally方法。2、CompletableFuture的get()方法是阻塞的CompletableFuture的get()方法是阻塞的如果使用它来获取异步调用的返回值需要添加超时时间。//反例 CompletableFuture.get(); //正例 CompletableFuture.get(5, TimeUnit.SECONDS);3、不建议使用默认线程池CompletableFuture代码中又使用了默认的「ForkJoin线程池」处理的线程个数是电脑「CPU核数-1」。在大量请求过来的时候处理逻辑复杂的话响应会很慢。一般建议使用自定义线程池优化线程池配置参数。4、自定义线程池时注意饱和策略CompletableFuture的get()方法是阻塞的我们一般建议使用future.get(5, TimeUnit.SECONDS)。并且一般建议使用自定义线程池。但是如果线程池拒绝策略是DiscardPolicy或者DiscardOldestPolicy当线程池饱和时会直接丢弃任务不会抛弃异常。因此建议CompletableFuture线程池策略最好使用AbortPolicy然后耗时的异步线程做好线程池隔离哈。