面试题答案
一键面试策略
- 合理设置线程池:
- 分析任务类型(CPU 密集型、I/O 密集型等)。对于 CPU 密集型任务,线程池大小一般设置为
CPU 核心数 + 1
;对于 I/O 密集型任务,线程池大小可适当增大,例如CPU 核心数 * 2
或更多,以充分利用等待 I/O 的时间。 - 使用
ThreadPoolExecutor
自定义线程池,设置合适的核心线程数、最大线程数、队列容量等参数。
- 分析任务类型(CPU 密集型、I/O 密集型等)。对于 CPU 密集型任务,线程池大小一般设置为
- 任务队列管理:
- 选择合适的任务队列。如
LinkedBlockingQueue
可设置有界队列,防止任务无限堆积耗尽内存;SynchronousQueue
不存储任务,直接将任务交给线程处理,适用于任务处理速度快且需要快速响应的场景。
- 选择合适的任务队列。如
- 任务优先级:
- 对于关键业务任务,可设置较高优先级,优先从队列中取出执行。在自定义任务类中实现
Comparable
接口,重写compareTo
方法来定义优先级规则,然后使用PriorityQueue
作为任务队列。
- 对于关键业务任务,可设置较高优先级,优先从队列中取出执行。在自定义任务类中实现
方法
- 创建线程池:
ExecutorService executor = new ThreadPoolExecutor( 10, // 核心线程数 20, // 最大线程数 10L, // 线程存活时间 TimeUnit.SECONDS, new LinkedBlockingQueue<>(100) // 队列容量 );
- 使用 CompletableFuture 结合线程池执行任务:
CompletableFuture.supplyAsync(() -> { // 任务逻辑 return result; }, executor);
监控和调整资源参数
- 监控:
- 使用 JMX(Java Management Extensions):
ThreadPoolExecutor
本身支持 JMX,可以通过 JMX 监控线程池的运行状态,如活跃线程数、已完成任务数、队列大小等。 - 自定义监控指标:在任务执行前后记录时间、任务数量等,例如:
long startTime = System.currentTimeMillis(); CompletableFuture.supplyAsync(() -> { // 任务逻辑 return result; }, executor)
- 使用 JMX(Java Management Extensions):
.thenApply(r -> { long endTime = System.currentTimeMillis(); System.out.println("Task took " + (endTime - startTime) + " ms"); return r; });
2. **调整**:
- 根据监控数据,如果队列经常满且任务响应时间长,可适当增加线程池大小或队列容量;如果活跃线程数长时间接近最大线程数且系统负载高,可能需要优化任务逻辑或降低任务提交频率。例如,通过动态修改 `ThreadPoolExecutor` 的核心线程数和最大线程数:
```java
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
executor.setCorePoolSize(15);
executor.setMaximumPoolSize(25);