面试题答案
一键面试可能导致任务饥饿的原因
- 线程池核心线程数过小:核心线程数设置过少,当任务量较大时,大部分任务只能在队列中等待,而核心线程一直处于忙碌状态,新提交的任务长时间得不到执行。
- 任务队列容量过大:如果任务队列容量设置过大,大量任务堆积在队列中,可能会导致优先级高或者需要及时处理的任务长时间等待。
- 非公平调度:默认情况下,线程池采用非公平调度算法,新提交的任务可能会抢占正在等待执行任务的线程,导致部分任务等待时间过长。
- 任务类型不均:如果线程池中既有耗时短的任务,又有耗时长的任务,并且没有进行合理区分调度,耗时短的任务频繁执行,而耗时长的任务容易被积压。
避免任务饥饿的方法
- 合理设置线程池参数
- 核心线程数:根据任务的性质和预估的并发量来合理设置核心线程数。对于CPU密集型任务,核心线程数可以设置为CPU核心数 + 1;对于I/O密集型任务,可以适当增加核心线程数,比如CPU核心数 * 2。
- 最大线程数:最大线程数要根据系统资源(如内存、CPU等)来设置,避免创建过多线程导致系统资源耗尽。
- 队列容量:根据任务的特点设置合适的队列容量。如果任务对及时性要求较高,队列容量不宜过大。
- 任务队列策略
- 使用优先级队列:自定义任务类实现
Comparable
接口,重写compareTo
方法定义任务优先级。在创建线程池时,使用PriorityBlockingQueue
作为任务队列。这样优先级高的任务会优先被执行。
- 使用优先级队列:自定义任务类实现
- 采用特定的调度算法
- 公平调度:可以通过设置
RejectedExecutionHandler
,并在其中实现公平调度逻辑。比如使用ThreadPoolExecutor.CallerRunsPolicy
,当线程池饱和时,由提交任务的线程自己来执行任务,从而保证任务的公平性。
- 公平调度:可以通过设置
代码示例
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
class PriorityTask implements Runnable, Comparable<PriorityTask> {
private int priority;
private String taskName;
public PriorityTask(int priority, String taskName) {
this.priority = priority;
this.taskName = taskName;
}
@Override
public void run() {
System.out.println("Executing task: " + taskName + " with priority: " + priority);
}
@Override
public int compareTo(PriorityTask other) {
return Integer.compare(this.priority, other.priority);
}
}
public class ThreadPoolTaskStarvationExample {
public static void main(String[] args) {
BlockingQueue<Runnable> taskQueue = new PriorityBlockingQueue<>();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
4,
10,
TimeUnit.SECONDS,
taskQueue,
new ThreadPoolExecutor.CallerRunsPolicy());
executor.submit(new PriorityTask(3, "Task C"));
executor.submit(new PriorityTask(1, "Task A"));
executor.submit(new PriorityTask(2, "Task B"));
executor.shutdown();
}
}
在上述代码中:
PriorityTask
类实现了Runnable
和Comparable
接口,通过compareTo
方法定义了任务的优先级。- 使用
PriorityBlockingQueue
作为线程池的任务队列,确保高优先级任务优先执行。 - 设置
ThreadPoolExecutor.CallerRunsPolicy
作为拒绝策略,在一定程度上保证公平性。