CompletableFuture
本身支持异步执行,可以通过指定线程池来控制任务的执行方式,也可以通过默认的公共线程池(ForkJoinPool.commonPool()
)来执行。你可以通过传递自定义的线程池,或是使用 Thread
类来启动异步任务,下面我将分别介绍如何使用这些方式来提交 CompletableFuture
任务。
1. 使用线程池执行任务
默认情况下,CompletableFuture
使用 ForkJoinPool.commonPool()
来执行异步任务。如果你想使用自定义的线程池来执行任务,可以通过重载的 supplyAsync
和 runAsync
方法来指定线程池。
例子:通过自定义线程池提交任务
import java.util.concurrent.*;
public class CompletableFutureWithThreadPool {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 创建自定义线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 使用自定义线程池提交任务
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("线程 ID: " + Thread.currentThread().getName());
return 42;
}, executorService);
// 获取结果
Integer result = future.get(); // 阻塞,等待结果
System.out.println("结果: " + result);
// 关闭线程池
executorService.shutdown();
}
}
在这个例子中,我们使用 Executors.newFixedThreadPool(3)
创建了一个具有 3 个线程的固定线程池,并通过将 executorService
作为参数传递给 supplyAsync()
来确保任务在该线程池中执行。
2. 通过 Thread
类启动异步任务
如果你想通过 Thread
类来启动 CompletableFuture
,可以使用 Thread
创建并启动一个线程,然后在该线程中执行异步任务。虽然这样不是 CompletableFuture
的标准做法,因为 CompletableFuture
通常与线程池配合使用,但你依然可以手动启动一个线程来执行任务。
例子:通过 Thread
类启动任务
import java.util.concurrent.CompletableFuture;
public class CompletableFutureWithThread {
public static void main(String[] args) throws InterruptedException {
// 创建线程
Thread thread = new Thread(() -> {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("线程 ID: " + Thread.currentThread().getName());
return 42;
});
future.thenAccept(result -> {
System.out.println("结果: " + result);
});
});
// 启动线程
thread.start();
// 等待线程执行完成
thread.join();
}
}
在这个例子中,我们显式地创建了一个 Thread
来启动一个新的线程,并在该线程中执行 CompletableFuture.supplyAsync()
任务。虽然这种方法可以实现功能,但与线程池相比,它没有线程池的资源管理优势。
3. 使用 runAsync()
提交任务
runAsync()
用于提交没有返回值的异步任务。如果你不关心返回值,只想执行某些操作,可以使用 runAsync()
。
例子:提交无返回值任务
import java.util.concurrent.*;
public class CompletableFutureWithRunAsync {
public static void main(String[] args) throws InterruptedException {
// 创建自定义线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 提交无返回值的异步任务
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("任务开始执行,线程 ID: " + Thread.currentThread().getName());
}, executorService);
// 等待任务完成
future.join();
// 关闭线程池
executorService.shutdown();
}
}
这里使用了 runAsync()
提交一个没有返回值的异步任务,并且指定了自定义线程池。
4. 组合多个任务(线程池或 Thread)
你也可以组合多个 CompletableFuture
,在多个线程中异步执行多个任务并组合它们的结果。可以使用 thenCombine()
或 thenCompose()
等方法来处理多个异步任务的结果。
例子:组合多个任务
import java.util.concurrent.*;
public class CompletableFutureWithMultipleTasks {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 创建自定义线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 创建第一个任务
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务 1 执行在: " + Thread.currentThread().getName());
return 5;
}, executorService);
// 创建第二个任务
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务 2 执行在: " + Thread.currentThread().getName());
return 10;
}, executorService);
// 组合任务 1 和 2
CompletableFuture<Integer> resultFuture = future1.thenCombine(future2, (result1, result2) -> result1 + result2);
// 获取最终结果
Integer result = resultFuture.get();
System.out.println("最终结果: " + result);
// 关闭线程池
executorService.shutdown();
}
}
总结
- 使用线程池:推荐使用
CompletableFuture.supplyAsync()
或CompletableFuture.runAsync()
并传入自定义的线程池。这样能确保任务的管理更具灵活性、可控性,并提高资源的利用率。 - 通过
Thread
启动任务:虽然你可以通过显式地创建并启动一个Thread
来执行任务,但这通常不如使用线程池高效,因为线程池会自动管理线程的生命周期,减少频繁创建和销毁线程的开销。 - 组合任务:通过
thenCombine()
、thenCompose()
等方法,可以轻松地组合多个异步任务。
CompletableFuture
与线程池的结合,使得异步编程更加高效、灵活、可读性强。