通常情况下,线程池会判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。但是如果有限队列已经满了,则会交给饱和策略来处理这个任务。
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(); } }
将上面代码的变量 selectedPolicy 设置为 ABORT_POLICY。
selectedPolicy = ABORT_POLICY
运行程序,执行结果如下:
如上所示,执行了三个任务,第四个提交的时候,直接抛出异常,并且JVM一直处于运行状态。任务0和任务1已提交就被线程池中的两个线程执行,任务2提交到阻塞队列等待执行,这个时候继续提交任务,由于队列已满,触发饱和策略,直接抛出异常。
将上面代码的变量 selectedPolicy 设置为 CALLER_RUNS_POLICY。
selectedPolicy = CALLER_RUNS_POLICY
运行程序,执行结果如下:
如上所示,所有任务都被执行,不会被抛弃也不会有异常抛出。任务0,1,2都在线程池中的线程执行。任务3,4则是由main线程执行。这说明,CallerRunsPolicy 饱和策略在队列已满的情况下,会把后面提交的任务给回调用者线程去执行。换句话说就是在调用exector的线程中运行新的任务。
将上面代码的变量 selectedPolicy 设置为 DISCARD_POLICY。
selectedPolicy = DISCARD_POLICY
运行程序,执行结果如下:
如上所示,只执行了任务0,1,3。其他任务直接被抛弃。所以 DiscardPolicy 正如其名字,简单粗暴,队列满后新任务通通都抛弃。
将上面代码的变量 selectedPolicy 设置为 DISCARD_OLDEST_POLICY。
selectedPolicy = DISCARD_OLDEST_POLICY
运行程序,执行结果如下:
如上所示,只执行了任务0,1,4。其他任务2,3直接被抛弃。所以 DiscardOldestPolicy 也可以说正如其名字,简单粗暴,队列满后等待最久的任务将被新任务替换。
以上就是 JDK 提供的四个饱和策略,本文到此完结。