面试题答案
一键面试任务管理方案设计与实现
在Java中,可以使用CompletableFuture
来管理多个存在依赖关系的异步任务。CompletableFuture
提供了丰富的方法来处理异步任务的依赖关系和组合。
以下是一个示例代码,展示如何管理基于supplyAsync
创建的存在依赖关系的异步任务:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AsyncTaskManagement {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(2);
// 任务A
CompletableFuture<Integer> taskA = CompletableFuture.supplyAsync(() -> {
System.out.println("Task A is running on thread: " + Thread.currentThread().getName());
// 模拟一些计算
return 42;
}, executor);
// 任务B依赖于任务A的结果
CompletableFuture<String> taskB = taskA.thenApplyAsync(result -> {
System.out.println("Task B is running on thread: " + Thread.currentThread().getName());
// 根据任务A的结果进行处理
return "The result from Task A is: " + result;
}, executor);
// 获取任务B的结果
String result = taskB.get();
System.out.println(result);
executor.shutdown();
}
}
supplyAsync
底层原理
- 线程调度:
supplyAsync
方法会使用ForkJoinPool.commonPool()
(默认情况下)来调度任务。ForkJoinPool
是Java 7引入的一种线程池,它采用工作窃取算法,允许线程从其他繁忙线程的任务队列中窃取任务,从而提高系统的整体利用率。- 如果传入自定义的
Executor
,则使用该Executor
来调度任务。例如在上述代码中,我们创建了一个固定大小的线程池ExecutorService
,supplyAsync
就会使用这个线程池来调度任务。
- 任务队列:
ForkJoinPool
内部维护了多个任务队列。每个工作线程都有自己的任务队列,当一个任务提交到ForkJoinPool
时,它会被放入某个工作线程的任务队列中。- 当一个线程完成了自己任务队列中的任务后,它会尝试从其他线程的任务队列中窃取任务,这种机制有助于平衡各个线程的负载,提高系统资源的利用率。
CompletableFuture
任务在完成后,会将结果或异常存储在自身的数据结构中,并通过内部的状态标识任务的完成情况。依赖该任务的其他CompletableFuture
任务会在其完成时被触发执行(例如thenApplyAsync
等方法注册的任务)。
通过上述方案和对supplyAsync
底层原理的理解,可以高效地管理存在依赖关系的异步任务,并提升整体执行效率。