有志者,事竟成。———《后汉书·耿弇传》
一个有志气的人,做事情是一定会成功的!
通常情况下,线程池会判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。但是如果有限队列已经满了,则会交给饱和策略来处理这个任务。
ThreadPoolExecutor 的饱和策略可以通过调用 setRejectedExecutionHandler 来修改。JDK 提供了下面几种不同的饱和策略。
- AbortPolicy
- CallerRunsPolicy
- DiscardPolicy
- DiscardOldestPolicy
下面我们举例来逐个来说明,先看下面代码,我们创建一个线程池,然后创建一个函数 getPolicy() 用于获取不同的饱和策略。通过传入不同的饱和策略参数变量选择不同的策略,通过执行结果来检查饱和策略的效果。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolRejectedPolicyTest{
//线程池大小
private static final int THREAD_SIZE = 2;
//任务数量
private static final int TASK_SIZE = 5;
//饱和策略
private static final int ABORT_POLICY = 0;//Abort 策略
private static final int CALLER_RUNS_POLICY = 1;//CallerRuns 策略
private static final int DISCARD_POLICY = 2;//Discard策略
private static final int DISCARD_OLDEST_POLICY = 3;//DiscardOlds策略
//策略选择变量,改这个值选择不同的饱和策略
private static int selectedPolicy = ABORT_POLICY;
@SuppressWarnings("unchecked")
public static void main(String[] args) {
//阻塞队列只能容纳一个任务
BlockingQueue Q = new LinkedBlockingDeque<>(1);
//创建一个固定线程的线程池
ThreadPoolExecutor service = new ThreadPoolExecutor(THREAD_SIZE,THREAD_SIZE,0L, TimeUnit.MILLISECONDS,Q);
//设置线程池的饱和策略
service.setRejectedExecutionHandler(getPolicy(selectedPolicy));
//提交任务
for(int i = 0; i < TASK_SIZE; i++) {
service.submit(new Task(i));
}
service.shutdown();
}
//任务
static class Task implements Runnable {
//任务ID
private int taskId;
public Task(int id) {
taskId = id;
}
@Override
public void run() {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
System.err.println("线程被中断" + e.getMessage());
}
//打印当前任务信息
System.out.println("Task " + taskId + " is running on " + Thread.currentThread().getName());
}
}
//饱和策略
static RejectedExecutionHandler getPolicy(int policy) {
switch (policy) {
case ABORT_POLICY:
return new ThreadPoolExecutor.AbortPolicy();
case CALLER_RUNS_POLICY:
return new ThreadPoolExecutor.CallerRunsPolicy();
case DISCARD_POLICY:
return new ThreadPoolExecutor.DiscardPolicy();
case DISCARD_OLDEST_POLICY:
return new ThreadPoolExecutor.DiscardOldestPolicy();
default:
break;
}
return new ThreadPoolExecutor.AbortPolicy();
}
}
AbortPolicy
将上面代码的变量 selectedPolicy 设置为 ABORT_POLICY。
selectedPolicy = ABORT_POLICY
运行程序,执行结果如下:
如上所示,执行了三个任务,第四个提交的时候,直接抛出异常,并且JVM一直处于运行状态。任务0和任务1已提交就被线程池中的两个线程执行,任务2提交到阻塞队列等待执行,这个时候继续提交任务,由于队列已满,触发饱和策略,直接抛出异常。
CallerRunsPolicy
将上面代码的变量 selectedPolicy 设置为 CALLER_RUNS_POLICY。
selectedPolicy = CALLER_RUNS_POLICY
运行程序,执行结果如下:
如上所示,所有任务都被执行,不会被抛弃也不会有异常抛出。任务0,1,2都在线程池中的线程执行。任务3,4则是由main线程执行。这说明,CallerRunsPolicy 饱和策略在队列已满的情况下,会把后面提交的任务给回调用者线程去执行。换句话说就是在调用exector的线程中运行新的任务。
DiscardPolicy
将上面代码的变量 selectedPolicy 设置为 DISCARD_POLICY。
selectedPolicy = DISCARD_POLICY
运行程序,执行结果如下:
如上所示,只执行了任务0,1,3。其他任务直接被抛弃。所以 DiscardPolicy 正如其名字,简单粗暴,队列满后新任务通通都抛弃。
DiscardOldestPolicy
将上面代码的变量 selectedPolicy 设置为 DISCARD_OLDEST_POLICY。
selectedPolicy = DISCARD_OLDEST_POLICY
运行程序,执行结果如下:
如上所示,只执行了任务0,1,4。其他任务2,3直接被抛弃。所以 DiscardOldestPolicy 也可以说正如其名字,简单粗暴,队列满后等待最久的任务将被新任务替换。
以上就是 JDK 提供的四个饱和策略,本文到此完结。