一、CompletableFuture的简介
-
CompletableFuture是Java 8中新增的一个类,它实现了CompletionStage接口和Future接口。CompletionStage接口是对Future接口的扩展,提供了更多的功能和灵活性。
-
CompletableFuture提供了一系列的方法,用于处理异步任务的结果。它可以让任务在后台线程中执行,并且可以通过回调函数来处理任务的结果。这种异步的方式可以提高程序的性能,避免阻塞主线程。
-
除了异步执行,CompletableFuture还提供了流式处理的能力。它的方法可以链式调用,这样可以对多个任务的结果进行处理,实现任务的串行或并行执行。这种流式处理的方式使得代码更加简洁和可读。
-
另外,CompletableFuture还提供了一些组合的方法,比如thenCombine()、thenCompose()、allOf()、anyOf()等。这些方法可以对多个任务的结果进行组合处理,实现更复杂的逻辑。
-
总的来说,CompletableFuture是Java 8中非常有用的一个特性,它提供了强大的处理异步任务结果的能力,使得Java在处理多任务的协同工作时更加顺畅便利。
二、CompletableFuture的常用Api分类
1. 创建CompletableFuture对象的静态方法:
这些方法可以创建一个CompletableFuture对象,并且可以指定任务的执行线程池。
1.1 supplyAsync
例如,CompletableFuture.supplyAsync()用于创建一个CompletableFuture对象,并在指定的线程池中执行一个有返回值的任务。
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureDemo {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello, CompletableFuture!";
});
try {
String result = future.get();
System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
-
这个示例中,我们使用CompletableFuture.supplyAsync()方法创建了一个CompletableFuture对象。在这个方法的参数中,我们传入了一个Lambda表达式,用于执行异步任务。在这个Lambda表达式中,我们模拟了一个耗时操作,然后返回了一个字符串。
-
然后,我们使用future.get()方法来获取异步任务的结果。由于CompletableFuture.supplyAsync()方法会在后台线程中执行任务,所以我们需要使用get()方法来等待任务的完成,并获取结果。在获取结果的过程中,get()方法可能会抛出InterruptedException或ExecutionException异常,我们需要进行相应的异常处理。
-
小结:CompletableFuture.supplyAsync()方法可以用来创建一个CompletableFuture对象,并在后台线程中执行一个异步任务。通过get()方法可以等待任务的完成,并获取结果。这种方式可以在不阻塞主线程的情况下执行耗时操作,提高程序的性能。同时,CompletableFuture还提供了丰富的方法来处理任务的结果和异常,使得异步编程更加灵活和简便。
2. 对CompletableFuture对象进行转换和处理的方法:
这些方法可以对CompletableFuture对象进行转换、处理和组合。
2.1 thenApplyAsync
thenApplyAsync()方法可以对CompletableFuture的结果进行转换。
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureDemo {
public static void main(String[] args) {
// 创建自定义线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 创建CompletableFuture对象,并指定线程池
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 异步任务
System.out.println("异步任务执行线程:" + Thread.currentThread().getName());
return "Hello";
}, executor);
// 使用thenApply方法处理任务结果,并指定线程池
CompletableFuture<Integer> result = future.thenApplyAsync(s -> {
// 对任务结果进行处理
System.out.println("任务结果处理线程:" + Thread.currentThread().getName());
return s.length();
}, executor);
// 等待任务结果并打印
try {
System.out.println("任务结果:" + result.get());
} catch (Exception e) {
e.printStackTrace();
}
// 关闭线程池
executor.shutdown();
}
}
-
在上面的示例中,首先创建了一个自定义线程池executor,它使用Executors.newFixedThreadPool(5)方法创建一个固定大小为5的线程池。
-
然后,创建了一个CompletableFuture对象future,并使用supplyAsync()方法指定了异步任务。通过thenApplyAsync()方法,对任务结果进行处理,并在thenApplyAsync()方法中指定了自定义线程池executor。
-
最后,使用result.get()方法等待任务结果,并打印结果。
-
通过自定义线程池,可以灵活地控制任务的执行方式。例如,可以根据实际情况调整线程池的大小,以提高任务的并发性能。同时,在任务执行过程中,可以通过打印线程名称来观察任务的执行线程,以及任务结果处理线程的变化。
2.2 thenAccept
thenAccept()方法可以对结果进行处理。
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureDemo {
public static void main(String[] args) {
// 创建一个自定义线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
// 创建一个CompletableFuture对象
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello";
}, executorService);
// 在CompletableFuture上应用thenAccept方法
completableFuture.thenAccept(result -> System.out.println(result + " World"));
// 关闭线程池
executorService.shutdown();
}
}
-
在这个示例中,我们使用CompletableFuture.supplyAsync方法创建了一个CompletableFuture对象,并传入自定义的线程池executorService作为参数。然后,我们在CompletableFuture对象上应用thenAccept方法,该方法接受一个Consumer函数,用于处理CompletableFuture的结果。在这个例子中,我们简单地打印出结果。
-
通过自定义线程池,我们可以灵活地控制任务的执行。在这个示例中,我们创建了一个固定大小的线程池,最多同时执行5个任务。这可以提高任务的并发性,并有效地利用系统资源。
-
分析:CompletableFuture是Java 8引入的一个强大的异步编程工具,它可以很方便地处理异步任务的结果。使用CompletableFuture的thenAccept方法,可以在CompletableFuture完成后执行对结果的操作。
-
自定义线程池可以更好地控制任务的执行,通过合理地设置线程池的大小,可以同时处理多个任务,提高并发性能。这对于处理大量耗时的任务非常有用,可以避免阻塞主线程。
-
然而,需要注意的是,如果不正确地使用自定义线程池,可能会导致资源浪费或性能下降。因此,在使用自定义线程池时,需要根据实际情况合理地设置线程池的大小,避免创建过多的线程。另外,需要及时关闭线程池,以释放资源。
-
总之,使用CompletableFuture的thenAccept方法,并结合自定义线程池,可以实现更高效的异步编程。通过合理地设置线程池大小,可以提高并发性能,并更好地利用系统资源。
2.3 thenCombine
thenCombine()方法可以组合多个CompletableFuture的结果。
import java.util.concurrent.CompletableFuture;
public class CompletableFutureDemo {
public static void main(String[] args) {
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 20);
CompletableFuture<Integer> combinedFuture = future1.thenCombine(future2, (result1, result2) -> result1 + result2);
combinedFuture.thenAccept(result -> System.out.println("Combined Result: " + result));
}
}
-
在这个例子中,我们创建了两个CompletableFuture对象:future1和future2。每个CompletableFuture对象都返回一个整数值。
-
然后,我们使用thenCombine方法将这两个CompletableFuture对象组合起来。这个方法接受两个参数:第一个参数是第一个CompletableFuture对象,第二个参数是第二个CompletableFuture对象。第三个参数是一个函数,用于将两个CompletableFuture的结果进行合并。
-
在这个例子中,我们将两个CompletableFuture对象的结果相加。
-
最后,我们使用thenAccept方法来处理合并后的结果,并将其打印出来。
-
这个例子展示了如何使用CompletableFuture的thenCombine方法来组合两个CompletableFuture对象的结果,并对结果进行处理。
-
这种组合操作可以在需要同时处理多个CompletableFuture对象的情况下非常有用,以提高并发性能。
3. 对CompletableFuture对象进行异常处理的方法:
这些方法可以对CompletableFuture对象的异常进行处理。
3.1 exceptionally
exceptionally()方法可以对CompletableFuture的异常进行处理,并返回一个默认值。
import java.util.concurrent.CompletableFuture;
public class CompletableFutureDemo {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 模拟一个可能抛出异常的操作
if (Math.random() < 0.5) {
throw new RuntimeException("Oops, something went wrong!");
}
return 10;
});
CompletableFuture<Integer> exceptionallyFuture = future.exceptionally(ex -> {
System.out.println("Exception occurred: " + ex.getMessage());
return -1;
});
exceptionallyFuture.thenAccept(result -> System.out.println("Result: " + result));
}
}
-
在这个例子中,我们创建了一个CompletableFuture对象future,它执行一个可能抛出异常的操作。在这个例子中,我们用Math.random()模拟了一个随机异常的情况。
-
然后,我们使用exceptionally方法来处理异常情况。exceptionally方法接受一个函数作为参数,该函数会在CompletableFuture出现异常时被调用,并返回一个默认的结果。
-
在这个例子中,我们将异常信息打印出来,并返回一个默认值-1作为结果。
-
最后,我们使用thenAccept方法来处理最终的结果,并将其打印出来。
-
这个例子展示了如何使用CompletableFuture的exceptionally方法来处理CompletableFuture对象可能抛出的异常情况。通过使用exceptionally方法,我们可以在出现异常时提供一个默认的结果,以便后续的处理。这种异常处理机制可以帮助我们更好地控制并发任务的执行流程。
4. 对多个CompletableFuture对象进行组合处理的方法:
这些方法可以对多个CompletableFuture对象进行组合处理。
4.1 allOf
allOf()方法可以等待所有CompletableFuture对象都完成。
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureDemo {
public static void main(String[] args) {
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 10;
});
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 20;
});
CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 30;
});
CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2, future3);
try {
allFutures.get();
System.out.println("All tasks completed!");
int result1 = future1.get();
int result2 = future2.get();
int result3 = future3.get();
System.out.println("Result 1: " + result1);
System.out.println("Result 2: " + result2);
System.out.println("Result 3: " + result3);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
-
在这个例子中,我们创建了三个CompletableFuture对象:future1、future2和future3。每个CompletableFuture对象都执行一个耗时的任务,通过调用Thread.sleep方法模拟延迟。
-
然后,我们使用CompletableFuture的allOf方法来等待所有CompletableFuture对象都完成。allOf方法返回一个CompletableFuture对象,表示所有任务的完成。
-
在等待所有任务完成后,我们通过调用get方法来获取每个任务的结果,并打印出来。
-
这个例子展示了如何使用CompletableFuture的allOf方法来同时执行多个CompletableFuture任务,并在所有任务都完成后进行后续操作。通过使用allOf方法,我们可以更好地控制并发任务的执行流程,并在所有任务完成后进行一些汇总或后续处理的操作。
4.2 anyOf
anyOf()方法可以等待任意一个CompletableFuture对象完成。
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureDemo {
public static void main(String[] args) {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Result from CompletableFuture 1";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Result from CompletableFuture 2";
});
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Result from CompletableFuture 3";
});
List<CompletableFuture<String>> futures = Arrays.asList(future1, future2, future3);
CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(futures.toArray(new CompletableFuture[futures.size()]));
try {
Object result = anyFuture.get();
System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
- 在这个示例中,我们创建了三个CompletableFuture对象来模拟异步任务,每个任务都会休眠一段随机时间后返回一个结果。
我们将这些CompletableFuture对象存储在一个列表中,并使用CompletableFuture的静态方法anyOf来创建一个新的CompletableFuture,该CompletableFuture将在任何一个原始CompletableFuture完成时完成。 - 在主线程中,我们使用anyFuture.get()方法来等待任何一个CompletableFuture完成,并获取其返回的结果。
5. 同步等待CompletableFuture对象完成的方法:
这些方法可以同步等待CompletableFuture对象的完成。
5.1 get#join
当使用CompletableFuture类时,可以使用get()和join()方法来获取计算结果。这两个方法之间的主要区别在于异常处理和返回值类型。
下面是一个使用CompletableFuture类的简单示例,展示了get()和join()方法的用法和区别:
import java.util.concurrent.*;
public class CompletableFutureDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 创建一个CompletableFuture对象
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
// 使用get()方法获取结果
String result1 = future.get();
System.out.println("Result from get(): " + result1);
// 使用join()方法获取结果
String result2 = future.join();
System.out.println("Result from join(): " + result2);
}
}
-
在这个示例中,我们创建了一个CompletableFuture对象,并使用supplyAsync()方法在后台线程中执行一个简单的任务,返回字符串"Hello"。然后我们分别使用get()和join()方法来获取计算结果。
-
区别如下:
- 异常处理:get()方法会抛出checked异常,需要使用try-catch块来处理可能的异常;而join()方法则会抛出unchecked异常,不需要显式处理异常。
- 返回值类型:get()方法返回一个泛型类型的结果,可以将其赋值给一个变量;
而join()方法返回一个与CompletableFuture对象关联的泛型类型的结果,不能直接赋值给一个变量。因此,如果只是想简单地获取结果,join()方法更加方便。
- 需要注意的是,get()和join()方法都是阻塞方法,即调用这些方法后,当前线程会一直等待,直到计算完成并返回结果。如果计算未完成,那么调用这些方法的线程将被阻塞。因此,在使用这些方法时要小心,以避免可能的死锁和性能问题。
三、CompletableFuture总结
CompletableFuture是Java 8引入的一个用于处理异步任务的工具类。它提供了一种简单而强大的方式来管理异步任务的执行和结果处理。
CompletableFuture具有以下特点和优势:
1. 异步执行:
CompletableFuture可以在后台线程中执行任务,不会阻塞主线程,提高了程序的执行效率。
2. 链式操作:
CompletableFuture支持链式操作,可以通过一系列的方法调用来对任务进行转换、处理和组合。这样可以方便地定义任务的处理流程,提高了代码的可读性和维护性。
3. 异常处理:
CompletableFuture提供了丰富的异常处理方法,可以对任务的异常进行处理。例如,可以通过exceptionally()方法指定任务失败时的默认返回值,通过handle()方法对任务的异常进行处理。
4. 组合处理:
CompletableFuture提供了多个方法来对多个CompletableFuture对象进行组合处理。例如,可以通过allOf()方法等待多个CompletableFuture对象都完成,通过anyOf()方法等待任意一个CompletableFuture对象完成。
5. 灵活性:
CompletableFuture提供了丰富的API,可以满足不同场景下的需求。通过合理使用这些方法,可以更加高效地处理异步任务的结果。
总之,CompletableFuture是一个功能强大且易于使用的工具类,可以帮助开发者更加方便地处理异步任务,提高程序的性能和可维护性。它是现代Java开发中不可或缺的一部分。