Appearance
线程池面试题
1. 线程池的基本概念
问题:什么是线程池?为什么要使用线程池?
答案:
- 线程池:管理多个线程的容器,可以重复使用线程。
- 作用:
- 减少线程创建和销毁的开销。
- 控制并发线程数。
- 提高系统响应速度。
- 提高线程的可管理性。
2. 线程池的参数
问题:线程池有哪些重要参数?
答案:
- corePoolSize:核心线程数,线程池中常驻的线程数。
- maximumPoolSize:最大线程数,线程池中允许的最大线程数。
- keepAliveTime:空闲线程的存活时间。
- unit:keepAliveTime的时间单位。
- workQueue:任务队列,用于存储等待执行的任务。
- threadFactory:线程工厂,用于创建线程。
- handler:拒绝策略,当任务队列满且线程数达到最大值时的处理策略。
3. 线程池的执行流程
问题:线程池的执行流程是怎样的?
答案:
- 如果线程数小于corePoolSize,创建新线程执行任务。
- 如果线程数等于corePoolSize,将任务放入队列。
- 如果队列已满且线程数小于maximumPoolSize,创建新线程执行任务。
- 如果队列已满且线程数等于maximumPoolSize,执行拒绝策略。
4. 线程池的拒绝策略
问题:线程池有哪些拒绝策略?
答案:
- AbortPolicy:抛出RejectedExecutionException异常(默认)。
- CallerRunsPolicy:由调用线程执行任务。
- DiscardPolicy:丢弃任务,不抛出异常。
- DiscardOldestPolicy:丢弃队列中最老的任务,然后重新提交任务。
5. 线程池的创建方式
问题:如何创建线程池?
答案:
- 使用Executors工厂类:
java
ExecutorService executor = Executors.newFixedThreadPool(10);
ExecutorService executor = Executors.newCachedThreadPool();
ExecutorService executor = Executors.newSingleThreadExecutor();
ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);- 使用ThreadPoolExecutor:
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, // corePoolSize
20, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS, // unit
new ArrayBlockingQueue<>(100), // workQueue
Executors.defaultThreadFactory(), // threadFactory
new ThreadPoolExecutor.AbortPolicy() // handler
);6. FixedThreadPool
问题:FixedThreadPool的特点是什么?
答案:
- 特点:
- 核心线程数和最大线程数相同。
- 使用无界队列。
- 线程数固定,不会创建新线程。
- 适合负载稳定的场景。
示例:
java
ExecutorService executor = Executors.newFixedThreadPool(10);7. CachedThreadPool
问题:CachedThreadPool的特点是什么?
答案:
- 特点:
- 核心线程数为0,最大线程数为Integer.MAX_VALUE。
- 使用SynchronousQueue。
- 线程空闲60秒后回收。
- 适合负载不稳定的场景。
示例:
java
ExecutorService executor = Executors.newCachedThreadPool();8. SingleThreadExecutor
问题:SingleThreadExecutor的特点是什么?
答案:
- 特点:
- 核心线程数和最大线程数都为1。
- 使用无界队列。
- 保证任务按顺序执行。
- 适合需要顺序执行任务的场景。
示例:
java
ExecutorService executor = Executors.newSingleThreadExecutor();9. ScheduledThreadPool
问题:ScheduledThreadPool的特点是什么?
答案:
- 特点:
- 可以执行定时任务。
- 可以执行周期性任务。
- 使用DelayedWorkQueue。
示例:
java
ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
// 延迟执行
executor.schedule(() -> {
System.out.println("Delayed task");
}, 1, TimeUnit.SECONDS);
// 周期性执行
executor.scheduleAtFixedRate(() -> {
System.out.println("Fixed rate task");
}, 0, 1, TimeUnit.SECONDS);
// 周期性执行(延迟计算)
executor.scheduleWithFixedDelay(() -> {
System.out.println("Fixed delay task");
}, 0, 1, TimeUnit.SECONDS);10. 线程池的关闭
问题:如何关闭线程池?
答案:
- shutdown:不再接受新任务,等待已提交的任务执行完毕。
- shutdownNow:不再接受新任务,尝试停止正在执行的任务,返回未执行的任务列表。
示例:
java
executor.shutdown();
executor.shutdownNow();
// 等待线程池关闭
executor.awaitTermination(60, TimeUnit.SECONDS);11. 线程池的监控
问题:如何监控线程池?
答案:
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(...);
// 获取线程池状态
int corePoolSize = executor.getCorePoolSize();
int maximumPoolSize = executor.getMaximumPoolSize();
int poolSize = executor.getPoolSize();
int activeCount = executor.getActiveCount();
long completedTaskCount = executor.getCompletedTaskCount();
int queueSize = executor.getQueue().size();
// 打印线程池状态
System.out.println("Core Pool Size: " + corePoolSize);
System.out.println("Maximum Pool Size: " + maximumPoolSize);
System.out.println("Pool Size: " + poolSize);
System.out.println("Active Count: " + activeCount);
System.out.println("Completed Task Count: " + completedTaskCount);
System.out.println("Queue Size: " + queueSize);12. 线程池的配置
问题:如何合理配置线程池?
答案:
- CPU密集型任务:
- 线程数 = CPU核心数 + 1
- IO密集型任务:
- 线程数 = CPU核心数 * 2
- 混合型任务:
- 根据实际情况调整
示例:
java
int cpuCoreCount = Runtime.getRuntime().availableProcessors();
int threadCount = cpuCoreCount + 1; // CPU密集型
int threadCount = cpuCoreCount * 2; // IO密集型13. 线程池的任务队列
问题:线程池的任务队列有哪些?
答案:
- ArrayBlockingQueue:有界队列,基于数组。
- LinkedBlockingQueue:无界队列,基于链表。
- SynchronousQueue:不存储元素,直接传递。
- PriorityBlockingQueue:优先级队列。
- DelayQueue:延迟队列。
14. 线程池的线程工厂
问题:如何自定义线程工厂?
答案:
java
ThreadFactory threadFactory = new ThreadFactory() {
private AtomicInteger threadNumber = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "my-thread-" + threadNumber.getAndIncrement());
thread.setDaemon(false);
thread.setPriority(Thread.NORM_PRIORITY);
return thread;
}
};
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, 20, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
threadFactory,
new ThreadPoolExecutor.AbortPolicy()
);15. 线程池的异常处理
问题:如何处理线程池中的异常?
答案:
java
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(() -> {
try {
throw new RuntimeException("Exception");
} catch (Exception e) {
e.printStackTrace();
}
});
// 使用Future
Future<?> future = executor.submit(() -> {
throw new RuntimeException("Exception");
});
try {
future.get();
} catch (Exception e) {
e.printStackTrace();
}
// 使用UncaughtExceptionHandler
ThreadFactory threadFactory = r -> {
Thread thread = new Thread(r);
thread.setUncaughtExceptionHandler((t, e) -> {
e.printStackTrace();
});
return thread;
};16. 线程池的Future
问题:Future的作用是什么?
答案:
- Future:表示异步计算的结果。
- 作用:
- 检查任务是否完成。
- 获取任务结果。
- 取消任务。
示例:
java
Future<String> future = executor.submit(() -> {
Thread.sleep(1000);
return "Result";
});
// 检查任务是否完成
boolean isDone = future.isDone();
// 获取任务结果
String result = future.get();
// 取消任务
boolean isCancelled = future.cancel(true);17. 线程池的CompletableFuture
问题:CompletableFuture的作用是什么?
答案:
- CompletableFuture:支持链式调用的Future。
- 作用:
- 支持回调。
- 支持组合。
- 支持异常处理。
示例:
java
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "Hello";
});
future.thenApplyAsync(s -> s + " World")
.thenAcceptAsync(System.out::println)
.exceptionally(e -> {
e.printStackTrace();
return null;
});18. 线程池的ForkJoinPool
问题:ForkJoinPool的作用是什么?
答案:
- ForkJoinPool:用于执行分治任务的线程池。
- 特点:
- 工作窃取算法。
- 适合递归任务。
- 适合大数据量计算。
示例:
java
ForkJoinPool forkJoinPool = new ForkJoinPool();
RecursiveTask<Integer> task = new RecursiveTask<Integer>() {
@Override
protected Integer compute() {
if (n <= 1) {
return n;
}
RecursiveTask<Integer> left = new RecursiveTask<Integer>() {
@Override
protected Integer compute() {
return compute(n - 1);
}
};
RecursiveTask<Integer> right = new RecursiveTask<Integer>() {
@Override
protected Integer compute() {
return compute(n - 2);
}
};
left.fork();
right.fork();
return left.join() + right.join();
}
};
Integer result = forkJoinPool.invoke(task);19. 线程池的饱和策略
问题:如何自定义线程池的饱和策略?
答案:
java
RejectedExecutionHandler handler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("Task rejected");
}
};
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, 20, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
Executors.defaultThreadFactory(),
handler
);20. 线程池的动态调整
问题:如何动态调整线程池的参数?
答案:
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(...);
// 调整核心线程数
executor.setCorePoolSize(20);
// 调整最大线程数
executor.setMaximumPoolSize(30);
// 调整空闲线程存活时间
executor.setKeepAliveTime(120, TimeUnit.SECONDS);
// 调整线程池大小
executor.prestartAllCoreThreads();21. 线程池的预热
问题:如何预热线程池?
答案:
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(...);
// 预热核心线程
executor.prestartAllCoreThreads();
// 预热单个核心线程
executor.prestartCoreThread();22. 线程池的优雅停机
问题:如何优雅停机线程池?
答案:
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(...);
// 不再接受新任务
executor.shutdown();
// 等待任务执行完毕
while (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.out.println("Waiting for tasks to complete...");
}
// 强制停机
List<Runnable> unfinishedTasks = executor.shutdownNow();23. 线程池的内存泄漏
问题:如何避免线程池的内存泄漏?
答案:
- 避免使用无界队列:使用有界队列。
- 避免任务阻塞:设置合理的超时时间。
- 避免线程泄漏:及时关闭线程池。
- 避免任务持有外部引用:使用弱引用。
24. 线程池的监控指标
问题:线程池有哪些监控指标?
答案:
- 核心线程数:corePoolSize
- 最大线程数:maximumPoolSize
- 当前线程数:poolSize
- 活跃线程数:activeCount
- 已完成任务数:completedTaskCount
- 队列大小:queueSize
- 任务总数:taskCount
25. 线程池的最佳实践
问题:使用线程池的最佳实践有哪些?
答案:
- 使用ThreadPoolExecutor而不是Executors。
- 设置合理的线程池参数。
- 使用有界队列避免内存溢出。
- 设置合理的拒绝策略。
- 监控线程池的状态。
- 及时关闭线程池。
- 使用Future获取任务结果。
- 使用CompletableFuture实现异步编程。
- 使用ForkJoinPool处理分治任务。
- 避免在任务中阻塞线程。
