面试题答案
一键面试1. 自定义线程池
在高并发场景下,使用默认的线程池可能无法满足需求,通过自定义线程池可以更好地控制资源。
- 理论依据:默认的线程池(如ForkJoinPool.commonPool())可能会被多个异步任务共享,在高负载下容易出现资源竞争。自定义线程池可以根据任务特点调整线程数量、队列容量等参数,提高资源利用率。
- 代码示例:
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class CompletableFutureCustomThreadPoolExample {
private static final AtomicInteger counter = new AtomicInteger();
private static final ExecutorService executor = new ThreadPoolExecutor(
5,
10,
10L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
r -> new Thread(r, "CustomThread-" + counter.incrementAndGet())
);
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
// 模拟任务
System.out.println("Task running in custom thread pool");
return "Result";
}, executor)
.thenAccept(result -> System.out.println("Result received: " + result))
.whenComplete((res, ex) -> executor.shutdown());
}
}
在上述代码中,创建了一个自定义的ThreadPoolExecutor
,指定了核心线程数为5,最大线程数为10,线程存活时间为10秒,任务队列容量为100,并为每个线程设置了自定义的名称。通过supplyAsync
方法将任务提交到自定义线程池执行。
2. 任务编排
合理编排任务可以避免不必要的资源浪费,提高系统性能。
- 理论依据:通过
thenApply
、thenCompose
、thenAccept
等方法对任务进行编排,可以按照逻辑顺序执行任务,避免所有任务同时竞争资源。例如,有些任务可能需要依赖其他任务的结果,通过链式调用可以确保在依赖任务完成后再执行后续任务。 - 代码示例:
import java.util.concurrent.*;
public class CompletableFutureChainingExample {
private static final ExecutorService executor = Executors.newFixedThreadPool(5);
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
System.out.println("Task1 starts");
return "Task1 result";
}, executor)
.thenApply(result1 -> {
System.out.println("Task2 starts with result: " + result1);
return "Task2 result";
})
.thenAccept(result2 -> System.out.println("Task2 result received: " + result2))
.whenComplete((res, ex) -> executor.shutdown());
}
}
在这个示例中,Task2
依赖于Task1
的结果,通过thenApply
方法将Task1
的结果传递给Task2
,保证了任务的顺序执行,减少了资源的无效竞争。
3. 批量任务处理与资源控制
对于大量的异步任务,可以采用批量处理的方式来控制资源。
- 理论依据:如果一次性提交过多任务,可能会瞬间耗尽资源。将任务分成多个批次,每次提交一定数量的任务,待部分任务完成释放资源后再提交下一批任务,可以有效避免资源耗尽。
- 代码示例:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;
public class CompletableFutureBatchProcessingExample {
private static final ExecutorService executor = Executors.newFixedThreadPool(5);
public static void main(String[] args) {
List<Integer> taskIds = new ArrayList<>();
for (int i = 0; i < 20; i++) {
taskIds.add(i);
}
int batchSize = 5;
for (int i = 0; i < taskIds.size(); i += batchSize) {
List<Integer> batch = taskIds.subList(i, Math.min(i + batchSize, taskIds.size()));
List<CompletableFuture<String>> futures = batch.stream()
.map(id -> CompletableFuture.supplyAsync(() -> {
System.out.println("Task " + id + " is running");
return "Task " + id + " result";
}, executor))
.collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRun(() -> System.out.println("Batch completed"));
}
executor.shutdown();
}
}
在上述代码中,将20个任务分成每批5个任务进行处理。通过CompletableFuture.allOf
方法等待一批任务全部完成后再进行下一批任务的提交,有效控制了资源的使用。
通过合理利用CompletableFuture的这些特性,能够优化资源利用,避免资源耗尽,提高系统在高并发场景下的性能和稳定性。