问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

线程池拒绝策略(超详细讲解)

创作时间:
作者:
@小白创作中心

线程池拒绝策略(超详细讲解)

引用
CSDN
1.
https://blog.csdn.net/2201_75456895/article/details/139240593

线程池在处理任务时,如果任务数量超过了线程池和阻塞队列的容量,就会触发拒绝策略。Java提供了四种内置的拒绝策略,每种策略都有其特定的行为。本文将通过代码示例详细讲解这四种策略:AbortPolicy、CallerRunsPolicy、DiscardPolicy和DiscardOldestPolicy。

共用四种拒绝策略分别为:AbortPolicy,CallerRunsPolicy,DiscardPolicy,DiscardOldestPolicy。它们都是ThreadPoolExecutor 类下的静态内部类,并都实现了 RejectExecutionHandler 接口

下面的 MyRunnable 类用来描述一个任务

class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 正在执行 " + System.currentTimeMillis());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}  

1. AbortPolicy:

触发该拒绝策略时,会直接抛出 RejectedExecutionException 异常;

public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 8, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.AbortPolicy());
        MyRunnable runnable1 = new MyRunnable();
        MyRunnable runnable2 = new MyRunnable();
        MyRunnable runnable3 = new MyRunnable();
        executor.execute(runnable1);
        executor.execute(runnable2);
        executor.execute(runnable3);
    }  

抛出异常之后,此时新的任务也无法继续完成了,但是在阻塞队列中存在的任务并非是新的任务;所以等待 2 秒(因为 MyRunnable 接口 重写的 run 方法 sleep 了 2 秒)之后,此时线程会继续执行阻塞队列中的任务;看下面这个情况:

Thread.sleep(3000);
        System.out.println(System.currentTimeMillis());
        MyRunnable runnable4 = new MyRunnable();
        executor.execute(runnable4);  

在上述代码的基础上,再添加以上代码,再次执行程序:

可以看到,本来应该是执行完阻塞队列的任务之后,主线程休眠 3 秒,执行阻塞队列的任务会休眠2 秒,所以主线程休眠 3 秒之后,线程应该是空闲状态,理应继续执行下面的逻辑,打印当前时间戳,并执行任务 4,但是使用的是 AbortPolicy 拒绝策略,抛出异常后,后续任务也不会执行了;

2. CallerRunsPolicy:

触发该拒绝策略时,添加的任务由添加任务的线程执行;

public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 8, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.CallerRunsPolicy());
        MyRunnable runnable1 = new MyRunnable();
        MyRunnable runnable2 = new MyRunnable();
        MyRunnable runnable3 = new MyRunnable();
        executor.execute(runnable1);
        executor.execute(runnable2);
        executor.execute(runnable3);
}  

由执行结果可以看到,第一个任务由线程池的核心线程执行, 第二个任务进入阻塞队列并等待执行,第三个任务会触发拒绝策略 CallerRunsPolicy,由添加任务的线程执行,显然此处是 main 线程添加的任务,故由 main 线程执行,核心线程经过 2 秒执行完毕后,继续执行阻塞队列中的任务;此时,如果有新任务再次添加,结果又会怎么样呢?同样添加上述代码,测试结果如下:

Thread.sleep(3000);
        System.out.println(System.currentTimeMillis());
        MyRunnable runnable4 = new MyRunnable();
        executor.execute(runnable4);  

可以看出,触发 CallerRunsPolicy 拒绝策略之后,再有新任务来时,仍然能正常执行;

3. DiscardOldestPolicy:

触发该拒绝策略时,会丢弃一个阻塞队列中最古老的任务,并将新的任务加入到阻塞队列中;

为了验证该拒绝策略,对 MyRunnable 类添加一个属性 name,并对 run 方法进行调整:

class MyRunnable implements Runnable{
    private String name;
    public MyRunnable(String name){
        this.name = name;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 正在执行 " + this.name + " 时间 " + System.currentTimeMillis());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}  
public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 8, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.DiscardOldestPolicy());
        MyRunnable runnable1 = new MyRunnable("第一个任务");
        MyRunnable runnable2 = new MyRunnable("第二个任务");
        MyRunnable runnable3 = new MyRunnable("第三个任务");
        MyRunnable runnable4 = new MyRunnable("第四个任务");
        executor.execute(runnable1);
        executor.execute(runnable2);
        executor.execute(runnable3);
        executor.execute(runnable4);
        Thread.sleep(3000);
        System.out.println("所有任务执行完毕");
    }  

由代码及运行结果可以看出,首先第一个任务来时,由核心线程执行,第二个任务来时,进入阻塞队列,第三个任务来时,线程全部都不空闲,并且阻塞队列已满,故会触发拒绝策略,丢弃第二个任务,并将第三个任务放入阻塞队列,第四个任务来时,仍然会触发拒绝策略,丢弃第三个任务,并将第四个任务放入阻塞队列,之后核心线程执行完第一个任务之后,会继续执行阻塞队列中的第四个任务;

4. DiscardPolicy:

触发该拒绝策略时,会直接丢弃掉新来的任务,即直接忽略;

public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 8, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.DiscardPolicy());
        MyRunnable runnable1 = new MyRunnable("第一个任务");
        MyRunnable runnable2 = new MyRunnable("第二个任务");
        MyRunnable runnable3 = new MyRunnable("第三个任务");
        executor.execute(runnable1);
        executor.execute(runnable2);
        executor.execute(runnable3);
        Thread.sleep(3000);
        System.out.println("所有任务执行完毕");
    }  

由代码及运行结果可以看出,首先第一个任务来时,由核心线程执行,第二个任务来时,进入阻塞队列,第三个任务来时,会触发 DiscardPolicy 拒绝策略,直接丢弃第三个任务,等待核心线程执行完第一个任务之后,会继续执行阻塞队列中的第二个任务;

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号