Java8 Stream之异步线程的使用和总结

概要

对于Java程序员来说,Java 8引入的Stream是一种处理集合数据的新方式,它提供了更简洁、更灵活的方法来执行各种数据操作,充分利用了计算机潜力。 如果要说Stream的有哪些优点简要说:
函数式编程风格,简化集合操作,并行处理能力,内置函数,更少的变量和中间集合。

ComplateFuture
//当前可用线程
int i = Runtime.getRuntime().availableProcessors();

使用CompetableFutures或者Stream并行计算的场景

  • 如果批量计算是密集型,并没有I/O,使用Stream平行处理
  • 如果批量计算密集型并且是I/O型,比如请求网络等,使用CompletableFuture更合适。

Java7时使用的Future

ExecutorService executor = Executors.newCachedThreadPool();
String name = "张三", message = "几个菜";
Future<String> future = executor.submit(() -> showInfo(name, message));
System.out.println("异步执行了");
//做其他事情
try {
  //必须返回结果的时候,get();可以设置等待时间。
  String s = future.get();
  System.out.println(s);
} catch (ExecutionException ee) {
  // 计算抛出一个异常
} catch (InterruptedException ie) {
  // 当前线程在等待过程中被中断
}

Java8升级为CompletableFuture

CompletableFuture.supplyAsync(() -> showInfo(product, product));

CompletableFuture批量执行

List<CompletableFuture<String>> priceFutures = menuList.stream()
  .map(shop -> CompletableFuture.supplyAsync(() -> shop.getName() + " price is " + shop.getPrice()))
  .collect(Collectors.toList());
//join是等待所有的异步执行完成
List<String> collect = priceFutures.stream().map(CompletableFuture::join).collect(toList());
collect.forEach(System.out::println);

CompletableFuture 是一个Java 8中引入的类,用于支持异步编程和非阻塞操作。下面是CompletableFuture的一些函数属性:

  1. 异步计算:CompletableFuture允许开发人员使用supplyAsync()runAsync()方法创建异步计算。
  2. 链式调用:CompletableFuture允许使用thenApply()thenAccept()thenRun()等方法来定义在异步操作完成后执行的操作。
  3. 合并操作:CompletableFuture提供了thenCombine()thenCompose()allOf()等方法来支持多个异步操作的合并。
  4. 异常处理:CompletableFuture允许使用exceptionally()handle()whenComplete()等方法来处理异步操作可能产生的异常。
  5. 超时处理:CompletableFuture提供了completeOnTimeout()orTimeout()等方法来处理异步操作超时的情况。
  6. 取消操作:CompletableFuture允许使用cancel()方法取消异步操作。
  7. 线程池管理:CompletableFuture提供了executor()方法来指定异步操作的执行线程池。

这些函数属性使得CompletableFuture成为一种非常强大和灵活的工具,可以用于实现复杂的异步编程逻辑。

  • CompletableFuture 的supllyAsync()和runAsync函数的区别

    • supplyAsync函数接受一个返回值的的函数作为参数,并返回一个CompletableFuture对象,该对象在异步计算完成后将包含函数的返回值。

      CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> this.showInfo("", ""));
      
    • runAsync () 方法接受一个没有返回值的函数作为参数, 并返回一个CompletableFuture对象,该对象在异步计算完成后不会包含任何返回值。

      CompletableFuture<Void> future = CompletableFuture.runAsync(this::testFuture);
      
    • 总之,supplyAsync又返回值,runAsync没有返回值。

  • CompleableFuture的thenCompose函数的使用

    • 函数的作用是整合多个CompletableFuture函数结果
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> " world!");
//可能还没有计算结束
CompletableFuture<String> composedFuture = future1.thenCompose(result1 -> future2.thenApply(result2 -> result1 + result2));

String result = composedFuture.get();
System.out.println(result); // 输出:Hello world!
  • CompleableFuture的thenCombine函数的使用。thenCombine是CompleableFuture中用于组合多个异步人的方法之一。它允许两个异步任务都完成之后,使用这两个任务的结果执行一个操作,并返回一个新的CompletableFuture对象,该对象包括最终的处理结果。
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 20);

CompletableFuture<Integer> combinedFuture = future1.thenCombine(future2, (result1, result2) -> result1 + result2);

Integer result = combinedFuture.get();
System.out.println(result); // 输出:30
  • CompletableFuture 函数allOf方法用于等待多个异步任务完成的方法之一。它允许将多个CompletableFuture对象组合在一起,并等待所有的异步执行完成以便下一步操作。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> " world!");
	
CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2);

allFutures.get(); // 等待所有异步任务完成

System.out.println(future1.get() + future2.get()); // 输出:Hello world!
  • CompleableFuture中的exceptionally,handle和whenComplete函数

    • exceptionally函数的使用,是异步调用异常的情况下的CompletableFuture的执行。如果没有异常则不会调用。
    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        // 执行异步任务,这里会抛出异常
        throw new RuntimeException("test exception");
    });
    
    CompletableFuture<Integer> result = future.exceptionally(ex -> {
        // 异步任务抛出异常时,执行该函数
        System.out.println("Caught exception: " + ex.getMessage());
        return 0; // 返回默认值
    });
    
    System.out.println(result.get()); // 输出:0
    
    
    • handle方法有异常捕捉功能也有可以在CompletableFuture完成之后出结果数据
    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 10);
    
    CompletableFuture<Void> result = future.whenComplete((res, ex) -> {
        if (ex != null) {
            // 异步任务抛出异常时,执行该函数
            System.out.println("Caught exception: " + ex.getMessage());
        } else {
            // 异步任务正常完成时,执行该函数
            System.out.println("Result: " + res);
        }
    });
    
    result.get(); // 等待异步任务完成后执行的操作完成
    
    • whenComplete() 方法与 handle() 方法类似,但不返回一个计算结果,而是返回一个 CompletableFuture<Void> 对象,表示异步任务完成后执行的操作已经完成。当异步任务完成时,它将调用一个函数来处理结果或异常情况,并在处理完结果或异常情况后,返回一个 CompletableFuture<Void> 对象。示例代码如下:

      CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 10);
      
      CompletableFuture<Void> result = future.whenComplete((res, ex) -> {
          if (ex != null) {
              // 异步任务抛出异常时,执行该函数
              System.out.println("Caught exception: " + ex.getMessage());
          } else {
              // 异步任务正常完成时,执行该函数
              System.out.println("Result: " + res);
          }
      });
      
      result.get(); // 等待异步任务完成后执行的操作完成
      
    • CompletableFuture的超时处理CompleteOnTimeout和orTimeout的方法。这两个方法都是用于设置超时操作的,可以在异步任务执行超时或者超时时间到期是返回一个默认值或抛出异常,从而避免程序长时间等待异步任务的执行结果。

      • completeOnTimeOut()方法可以在指定的超时时间内执行异步任务,并在超时时间到期时返回一个默认值。
      CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
          // 模拟异步任务执行需要 5 秒钟
          try {
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          return "Hello";
      });
      
      CompletableFuture<String> result = future.completeOnTimeout("Default", 3, TimeUnit.SECONDS);
      System.out.println(result.get()); // 输出:Default
      
      
      • orTimeout() 方法可以在指定的超时时间内执行异步任务,并在超时时间到期时抛出一个 TimeoutException 异常。示例代码如下:

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
          // 模拟异步任务执行需要 5 秒钟
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Hello";
        });
        
        CompletableFuture<String> result = future.orTimeout(3, TimeUnit.SECONDS);
        System.out.println(result.get()); // 抛出 TimeoutException 异常
        
  • CompletableFuture取消操作cancel()

    • 是的,CompletableFuture 允许使用 cancel() 方法来取消异步任务的执行。在某些情况下,可能需要取消异步任务的执行,例如在异步任务执行超时、用户主动取消任务、系统资源不足等情况下。

      调用 cancel() 方法可以中断异步任务的执行,并抛出一个 CancellationException 异常,表示异步任务已经被取消。示例代码如下:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 模拟异步任务执行需要 10 秒钟
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Hello";
});

boolean isCancelled = future.cancel(true);
System.out.println(isCancelled); // 输出:true

try {
    String result = future.get();
    System.out.println(result);
} catch (Exception e) {
    e.printStackTrace(); // 抛出 CancellationException 异常
}

在上面的例子中,使用 cancel(true) 方法取消异步任务的执行,并返回一个 boolean 值表示任务是否成功取消。然后,调用 future.get() 方法获取异步任务的执行结果,由于任务已经被取消,将抛出 CancellationException 异常。

需要注意的是,cancel() 方法只有在异步任务尚未开始执行或正在执行但还未完成时才能成功取消任务。如果任务已经执行完成,则调用 cancel() 方法将不会有任何效果。另外,如果异步任务已经被取消或已经完成,调用 cancel() 方法也将不会有任何效果

  • CompletableFuture设置线程池

    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
      System.out.println("异步任务开始执行,线程名:" + Thread.currentThread().getName());
      // 模拟异步任务执行需要 3 秒钟
      try {
        Thread.sleep(3000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      return "Hello";
    }, executor);
    
    future.thenAccept(result -> {
      System.out.println("异步任务执行完成,结果为:" + result + ",线程名:" + Thread.currentThread().getName());
    });
    
    System.out.println("主线程执行完成,线程名:" + Thread.currentThread().getName());
    
    executor.shutdown();
    

总结

  • 当需要多个远程I/O操作时, 同步执行多个远程请求是在效率低下。采用异步操作CompletableFuture。 不需要在等待第一个请求完成就行下一步,可以同时进行待需要的时候get一下。
  • 多个异步请求的时候,可以设置线程池Executors.newCachedThreadPool(),也可以不设置,使用默认的CompletableFuture使用的线程池。
  • 批量异步请求的时候,使用join等待所有请求完成。或者使用allOf等待完成。
  • 异步请求之间的执行链,thenApply, thenAccept,thenRun顺序完成需求。
  • 多个异步之间的计算应用, thenCompose(计算结果参与另一个async计算),thenCombine (多个计算结果运算)
  • 计算过程中出现的问题怎么办。两个办法:exceptionally将异常进行处理并返回设置的默认值。handle和whenComplete都可以处理异常并且再次进行计算。但是前者有返回值,后者只能计算不能返回。
  • 如果无法等待异步很长时间就要中断请求怎么处理?completeOnTimeout()orTimeout()
  • 取消计算怎么整?cancel一下吧。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值