好的,下面我将给出一个包含拒绝策略的线程池模拟代码。当任务队列已满且线程池已达到最大线程数时,将使用拒绝策略来处理新提交的任务。拒绝策略可以是抛异常、丢弃任务、调用者执行任务等。
我们将修改之前的线程池代码,使其支持以下功能:
- 最大任务队列长度:当任务队列满了,不能再添加新的任务。
- 拒绝策略:当任务无法提交时,我们可以选择不同的拒绝策略处理这种情况。
代码实现:带有拒绝策略的线程池
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
class SimpleThreadPoolWithRejectPolicy {
private final List<Worker> workers; // 存储线程池中的线程
private final LinkedList<Runnable> taskQueue; // 任务队列
private final int maxTasks; // 最大任务队列长度
private final RejectPolicy rejectPolicy; // 拒绝策略
private volatile boolean isShutdown = false; // 用于标记线程池是否关闭
// 定义拒绝策略接口
public interface RejectPolicy {
void reject(Runnable task);
}
// 构造函数,接受最大线程数和任务队列的最大大小
public SimpleThreadPoolWithRejectPolicy(int poolSize, int maxTasks, RejectPolicy rejectPolicy) {
this.taskQueue = new LinkedList<>();
this.maxTasks = maxTasks;
this.rejectPolicy = rejectPolicy;
this.workers = new LinkedList<>();
for (int i = 0; i < poolSize; i++) {
Worker worker = new Worker();
workers.add(worker);
worker.start();
}
}
// 提交任务
public synchronized void submit(Runnable task) {
if (isShutdown) {
throw new IllegalStateException("Thread pool is already shut down");
}
// 如果任务队列已满,则执行拒绝策略
if (taskQueue.size() >= maxTasks) {
rejectPolicy.reject(task);
} else {
taskQueue.add(task);
notify(); // 唤醒工作线程
}
}
// 关闭线程池
public synchronized void shutdown() {
isShutdown = true;
for (Worker worker : workers) {
worker.interrupt(); // 中断所有工作线程
}
}
private class Worker extends Thread {
public void run() {
while (true) {
Runnable task;
synchronized (SimpleThreadPoolWithRejectPolicy.this) {
while (taskQueue.isEmpty() && !isShutdown) {
try {
SimpleThreadPoolWithRejectPolicy.this.wait(); // 等待新任务
} catch (InterruptedException e) {
return; // 线程被中断,退出
}
}
task = taskQueue.poll(); // 从任务队列中取任务
}
if (task != null) {
task.run(); // 执行任务
}
if (isShutdown && taskQueue.isEmpty()) {
return; // 线程池关闭且无任务时退出
}
}
}
}
}
代码中的主要改进
RejectPolicy
接口:定义了一个拒绝策略接口,当任务队列满时,通过此接口处理新提交的任务。submit()
方法:提交任务时检查任务队列是否已满,若满则调用指定的拒绝策略处理新任务。
三种常见拒绝策略实现
public class RejectPolicies {
// 抛出异常的拒绝策略
public static final SimpleThreadPoolWithRejectPolicy.RejectPolicy ABORT_POLICY = task -> {
throw new RejectedExecutionException("Task " + task + " rejected due to full queue.");
};
// 直接丢弃任务的拒绝策略
public static final SimpleThreadPoolWithRejectPolicy.RejectPolicy DISCARD_POLICY = task -> {
System.out.println("Task " + task + " is discarded.");
};
// 调用者执行任务的拒绝策略
public static final SimpleThreadPoolWithRejectPolicy.RejectPolicy CALLER_RUNS_POLICY = task -> {
System.out.println("Task " + task + " is running in caller thread.");
task.run();
};
}
使用示例
我们将创建一个最大任务队列长度为2的线程池,并使用不同的拒绝策略来演示效果。
public class CustomThreadPoolWithRejectPolicyDemo {
public static void main(String[] args) {
// 使用丢弃策略,最大线程数为2,任务队列最多允许2个任务
SimpleThreadPoolWithRejectPolicy pool = new SimpleThreadPoolWithRejectPolicy(2, 2, RejectPolicies.DISCARD_POLICY);
// 提交5个任务,超过队列长度的任务将被丢弃
for (int i = 1; i <= 5; i++) {
final int taskId = i;
pool.submit(() -> {
System.out.println("Task " + taskId + " is running by thread " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟任务耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Task " + taskId + " completed.");
});
}
// 关闭线程池
pool.shutdown();
}
}
运行结果示例
Task 1 is running by thread Thread-0
Task 2 is running by thread Thread-1
Task java.util.concurrent.FutureTask@1540e19d is discarded.
Task java.util.concurrent.FutureTask@677327b6 is discarded.
Task java.util.concurrent.FutureTask@14ae5a5 is discarded.
Task 1 completed.
Task 2 completed.
解释:最大任务队列长度为2,当提交第3个任务时,任务队列已满,采用丢弃策略处理,后续任务被丢弃。
其他拒绝策略的效果
-
Abort Policy:
- 当任务队列已满时抛出
RejectedExecutionException
异常。 - 适用于不允许任务丢失的场景,通过异常处理机制提醒开发者队列已满。
- 当任务队列已满时抛出
-
Caller Runs Policy:
- 直接由调用者线程执行被拒绝的任务,避免任务丢失,但可能拖慢主线程执行。
- 适用于轻量任务且不能丢弃任务的场景。
线程池中的拒绝策略使用场景
-
高并发任务处理:在某些高并发场景下,系统可能无法处理所有任务,线程池中的拒绝策略可以帮助避免系统崩溃。例如:
- 金融交易系统:不能丢弃关键任务,必须抛出异常警告并进行人工干预。
- 日志系统:可以丢弃部分非关键的日志任务,防止队列溢出导致系统崩溃。
-
限流控制:在请求暴增时,线程池通过拒绝策略来限制同时执行的任务数量,避免资源耗尽或服务器过载。
-
调用者线程执行:适用于临时高峰压力,采用调用者线程执行任务,能分摊任务压力,避免任务积压。
总结
通过添加拒绝策略,线程池能够有效处理任务过载情况。不同的拒绝策略适用于不同的业务场景,开发者可以根据需求选择合适的策略。