面试题答案
一键面试优化策略
- 合理配置线程池参数:
- 核心线程数:根据任务类型和系统资源确定。对于CPU密集型任务,核心线程数可设置为CPU核心数 + 1;对于I/O密集型任务,核心线程数可设置为CPU核心数 * 2 或更高,具体根据I/O等待时间与CPU计算时间的比例调整。
- 最大线程数:设置一个合理的上限,避免过多线程导致系统资源耗尽。通常根据系统可用内存、CPU核心数以及任务执行特点来确定。
- 队列容量:选择合适的任务队列。无界队列(如
LinkedBlockingQueue
)可防止任务拒绝,但可能导致内存耗尽;有界队列(如ArrayBlockingQueue
)可以控制内存使用,但需要合理设置容量,避免任务拒绝。
- 线程复用:利用线程池本身的线程复用机制,减少线程创建和销毁的开销。Executor框架中的线程池默认支持线程复用。
- 任务优先级处理:如果任务有不同的优先级,可以使用
PriorityBlockingQueue
作为任务队列,并为任务实现Comparable
接口,按照优先级排序。高优先级任务优先执行,从而提高整体响应时间。 - 线程池监控与动态调整:
- 监控:通过
ThreadPoolExecutor
提供的方法(如getActiveCount
、getCompletedTaskCount
等)实时监控线程池的运行状态,包括活动线程数、已完成任务数、队列大小等。 - 动态调整:根据监控数据动态调整线程池参数。例如,当队列中的任务数量持续增加且接近队列容量时,可以适当增加线程数;当活动线程数过多且队列中任务很少时,可以适当减少线程数。
- 监控:通过
- 避免线程上下文切换开销:尽量减少锁的使用,使用无锁数据结构(如
ConcurrentHashMap
)。对于一些必须使用锁的场景,使用粒度更小的锁,减少锁的竞争。
Java代码实现
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CustomThreadPool {
// 假设CPU核心数为4
private static final int CPU_CORES = Runtime.getRuntime().availableProcessors();
// 核心线程数,假设为I/O密集型任务,设置为CPU核心数的2倍
private static final int CORE_POOL_SIZE = CPU_CORES * 2;
// 最大线程数
private static final int MAX_POOL_SIZE = CPU_CORES * 4;
// 队列容量
private static final int QUEUE_CAPACITY = 100;
// 线程存活时间
private static final long KEEP_ALIVE_TIME = 10;
public static void main(String[] args) {
// 使用PriorityBlockingQueue作为任务队列,支持任务优先级
BlockingQueue<Runnable> taskQueue = new PriorityBlockingQueue<>();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
taskQueue,
new ThreadPoolExecutor.CallerRunsPolicy());
// 提交任务
for (int i = 0; i < 10; i++) {
executor.submit(new Task(i));
}
// 关闭线程池
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("Pool did not terminate");
}
}
} catch (InterruptedException ie) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
// 实现具有优先级的任务
static class Task implements Runnable, Comparable<Task> {
private final int priority;
Task(int priority) {
this.priority = priority;
}
@Override
public void run() {
System.out.println("Task with priority " + priority + " is running.");
}
@Override
public int compareTo(Task other) {
return Integer.compare(this.priority, other.priority);
}
}
}
在上述代码中:
- 首先根据系统CPU核心数设置了核心线程数和最大线程数。
- 使用
PriorityBlockingQueue
作为任务队列,使任务能够按照优先级执行。 - 定义了实现
Comparable
接口的Task
类,以便在PriorityBlockingQueue
中按优先级排序。 - 展示了如何提交任务并关闭线程池,同时处理了线程池关闭过程中的异常。