CompletableFuture类
- CompletableFuture 是 Java 平台的一个类,用于处理异步编程和并发任务。它是 Java 8 中引入的一种 Future 类型,用于解决传统的 Future 类型在处理异步操作时的限制。
- CompletableFuture 允许你以非阻塞的方式执行异步任务,然后在任务完成时执行回调操作。这使得编写异步和并发代码更加简单和可读,而不需要显式地管理线程和同步。以下是一些 CompletableFuture 的主要特点和用法:
- 异步任务:你可以将任务包装在 CompletableFuture 中,然后在后台线程中执行,而不会阻塞主线程。
- 链式操作:你可以使用方法链来定义一系列操作,以便在异步任务完成后执行各种操作,例如转换、组合和异常处理。
- 异常处理:你可以轻松处理异步任务中发生的异常,以确保你的程序不会崩溃。合并多个 CompletableFuture:你可以将多个 CompletableFuture 组合成一个,以便等待它们 全部完成或者只要有一个完成就执行下一步操作。
- 定时操作:你可以使用 CompletableFuture 来执行定时操作,例如等待一定时间后执行某个任务。
下面是测试代码: 异步任务
@Test
public static void main(String[] args) {
// 创建一个CompletableFuture来执行异步任务
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 模拟一个耗时的任务,例如网络请求或数据库查询
try {
Thread.sleep(5000); // 5秒延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
return 42;
});
// 注册一个回调,当异步任务完成时触发
future.thenAccept(result -> {
System.out.println("异步任务已完成,结果: " + result);
});
// 主线程可以继续执行其他操作
System.out.println("主线程继续工作...");
// 检查任务是否已完成
if (future.isDone()) {
System.out.println("已完成");
} else {
System.out.println("未完成");
}
// 等待异步任务完成
try {
int result = future.get(); // 阻塞等待异步任务的结果
System.out.println("主线程中的异步任务结果: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
执行结果:
@Test
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3); // 创建一个包含3个线程的线程池
CompletableFuture<Void> task1 = CompletableFuture.runAsync(() -> {
// 任务1的处理逻辑
System.out.println("任务1在执行");
}, executor);
CompletableFuture<Void> task2 = CompletableFuture.runAsync(() -> {
// 任务2的处理逻辑
System.out.println("任务2在执行");
}, executor);
CompletableFuture<Void> task3 = CompletableFuture.runAsync(() -> {
// 任务3的后台处理逻辑
try {
Thread.sleep(10000); // 10秒延迟
} catch (InterruptedException e) {
}
System.out.println("任务3在执行");
}, executor);
CompletableFuture<Void> allTasks = CompletableFuture.allOf(task1,task2);
// 当任务1和任务2完成后执行后续操作
allTasks.thenRun(() -> {
System.out.println("任务1和任务2完成,不等待任务3");
});
// 等待所有任务完成
try {
allTasks.get();
} catch (Exception e) {
e.printStackTrace();
}
executor.shutdown();
System.out.println("执行完成");
}
执行结果:
CompletableFuture代码中的应用
- 起因是客户需要在同步JIT类型的采购订单时要自动执行交期已确认部分的流程,无需供应商在前台进行手动点击
- 这部分代码加入,结果导致返回时间增加,造成erp拿不到返回结果
- 解决:将这部分代码加入异步任务执行 ,不影响主线程的任务执行,转为后台执行
- ,使用CompletableFuture中的runAsync(()方法执行,runAsync(()执行无需返回结果
CompletableFuture类的一些方法
- thenApply(Function<? super T,? extends U> fn):当一个 CompletableFuture 完成时,将结果传递给指定的函数,生成一个新的 CompletableFuture 对象。
- thenAccept(Consumer<? super T> action):当一个 CompletableFuture 完成时,对结果执行指定的操作,但不生成新的结果。
- thenRun(Runnable action):当一个 CompletableFuture 完成时,执行指定的操作,不关心之前的计算结果。
- thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn):将两个 CompletableFuture 的结果传递给指定的函数,并生成一个新的 CompletableFuture。
- thenCompose(Function<? super T,? extends CompletionStage<U>> fn):允许一个 CompletableFuture 依赖于另一个 CompletableFuture 的结果,并生成一个新的 CompletableFuture。
- exceptionally(Function<Throwable, ? extends T> fn):当 CompletableFuture 异常完成时,执行指定的函数来处理异常,并返回一个新的 CompletableFuture。
- handle(BiFunction<? super T, Throwable, ? extends U> fn):当 CompletableFuture 完成时,执行指定的函数处理结果或异常,并返回一个新的 CompletableFuture。
- whenComplete(BiConsumer<? super T, ? super Throwable> action):当 CompletableFuture 完成时,执行指定的操作,既能处理结果也能处理异常,但不返回新的 CompletableFuture。
- thenApplyAsync(Function<? super T,? extends U> fn) 和 thenApplyAsync(Function<? super T,? extends U> fn, Executor executor):在异步线程上应用指定的函数,生成新的 CompletableFuture。
- thenAcceptAsync(Consumer<? super T> action) 和 thenAcceptAsync(Consumer<? super T> action, Executor executor):在异步线程上执行指定的操作,不生成新的结果。
- thenRunAsync(Runnable action) 和 thenRunAsync(Runnable action, Executor executor):在异步线程上执行指定的操作,不关心之前的计算结果。
- allOf(CompletableFuture<?>... cfs) 和 anyOf(CompletableFuture<?>... cfs):用于等待一组 CompletableFuture 完成,分别等待所有或任何一个 CompletableFuture 完成。
这些是 CompletableFuture 类的一些常用方法,但并不限于这些。CompletableFuture 类提供了丰富的方法来处理异步任务和组合结果。可以根据需要选择适合的任务和流程的方法。
stream和parallelStream的区别
- parallelStream并行流就是一个把内容分成多个数据块,并用不不同的线程分别处理每个数据块的流。最后合并每个数据块的计算结果。处理的线程数就是机器的处理器核心数。
- 串行流 (stream):
- stream 方法返回一个串行流,它在单线程中顺序处理数据。
- 串行流适用于那些不需要并行处理的简单操作,如过滤、映射、排序等。
- 串行流在单线程中执行,适用于处理较小的数据集,通常不涉及并行处理。
- 并行流 (parallelStream):
- parallelStream 方法返回一个并行流,它能够充分利用多核处理器并行处理数据。
- 并行流适用于那些可以分解成独立任务并并行执行的操作,如大规模数据的并行处理、复杂计算等。
- 并行流在背后使用了多线程处理数据,适用于处理大规模数据集,提高了处理速度。
@Test
public static void main(String[] args) {
List<Integer> intList = mockData();
useStream(intList);
useParallelStream(intList);
}
public static List<Integer> mockData() {
List<Integer> intList = new ArrayList<Integer>();
for (int i = 0; i < 1000000; i++) {
intList.add(i);
}
return intList;
}
public static void useStream(List<Integer> integerList) {
long start = System.currentTimeMillis();
long count = integerList.stream().filter(x -> (x%2==0)).count();
System.out.println(count);
long end = System.currentTimeMillis();
System.out.println("useStream cost:" + (end - start));
}
public static void useParallelStream(List<Integer> integerList) {
long start = System.currentTimeMillis();
long count = integerList.parallelStream().filter(x -> (x%2==0)).count();
System.out.println(count);
long end = System.currentTimeMillis();
System.out.println("useParallelStream cost:" + (end - start));
}
执行结果:
stream和parallelStream代码中的应用
- 执行代码:
下面分别对普通foreach循环,stram 以及parallelStream的对20条以及100条数据的执行速度
- 通过程序中的执行速度对比发现,stram流和普通foreach循环在小数据量上差别不大,但是使用parallelStream的时候执行速度差不多相差了十倍
- 在使用并行流要注意 线程安全性,确保没有竞态条件和线程安全问题以及确保您的数据源适合并行处理 ,另外并不是所有任务都适合并行处理。在某些情况下,串行流可能会更快,因为并行流引入了额外的开销,例如线程管理和合并结果。您应该根据任务的性质和数据量来决定是否使用并行流。