[Java并发编程实战]线程池的饱和策略

有志者,事竟成。———《后汉书·耿弇传》
一个有志气的人,做事情是一定会成功的!

通常情况下,线程池会判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。但是如果有限队列已经满了,则会交给饱和策略来处理这个任务。

ThreadPoolExecutor 的饱和策略可以通过调用 setRejectedExecutionHandler 来修改。JDK 提供了下面几种不同的饱和策略。

  1. AbortPolicy
  2. CallerRunsPolicy
  3. DiscardPolicy
  4. 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 提供的四个饱和策略,本文到此完结。

猜你喜欢

转载自blog.csdn.net/amd123456789/article/details/80836088