Java中线程池相关的七个参数及其拒绝策略

9 篇文章 0 订阅

目录

1、线程池的七个参数

2、拒绝策略

拒绝策略触发的条件

2.1 AbortPolicy(中止策略)

2.2 CallerRunsPolicy(调用者运行策略)

2.3 DiscardPolicy(丢弃策略)

2.4 DiscardOldestPolicy(丢弃最老策略)


1、线程池的七个参数

在Java1.5版本以后中,新增加了JUC并发包,线程池也包含在其中。线程池的七个参数是指线程池的相关配置参数,用来控制线程池的行为和性能。这些参数包括:

1. 核心线程数(corePoolSize):指线程池中保持活动状态的最少线程数,即使在空闲时也不会被回收。当有新的任务提交时,如果当前活动线程数小于核心线程数,则会创建新的线程来处理任务。

2. 最大线程数(maximumPoolSize):线程池中允许存在的最大线程数,当任务队列已满且当前活动线程数小于最大线程数时,线程池会创建新的线程,直到达到最大线程数。

3. 空闲线程存活时间(keepAliveTime):指空闲线程在被回收之前可以等待新任务的最长时间。当线程池中的线程数量超过核心线程数,并且处于空闲状态时,这些多余的线程在超过指定时间后会被回收销毁。

4. 时间单位(unit):空闲线程存活时间的单位,可以是秒、毫秒、分钟等。

5. 任务队列(workQueue):用于存放等待执行任务的阻塞队列。可以选择不同的队列类型来实现不同的调度策略。当线程池中的线程都在工作且任务队列已满时,新的任务会被拒绝执行。

6. 线程工厂(threadFactory):用于创建新线程的工厂,可以自定义线程的名称、优先级等属性。

7. 拒绝策略(rejectedExecutionHandler:当线程池已满并且任务无法执行时的处理策略,如何处理新提交的任务,有以下几种选择:
   - ThreadPoolExecutor.AbortPolicy:默认策略,抛出RejectedExecutionException异常。
   - ThreadPoolExecutor.CallerRunsPolicy:由调用线程执行该任务。
   - ThreadPoolExecutor.DiscardPolicy:直接丢弃无法执行的任务。
   - ThreadPoolExecutor.DiscardOldestPolicy:丢弃最早提交的任务,然后尝试执行新任务。

通过合理调优这些参数,可以根据具体的业务需求来提升线程池的性能和效率。需要根据实际情况进行选择和配置,以平衡系统资源和任务处理能力。

2、拒绝策略

拒绝策略触发的条件

拒绝策略在线程池已满且无法接受新任务时会被触发。具体触发条件取决于线程池的配置参数,主要包括以下两个条件:

1. 线程池的线程数量已经达到了最大线程数(maximumPoolSize):线程池中的活动线程数量已经达到了最大线程数,无法再创建新的线程来执行任务。

2. 任务队列(workQueue)已满:线程池中的任务队列已经满载,无法接受新的任务。

当同时满足这两个条件时,拒绝策略会被触发,根据所选的拒绝策略进行相应的处理。例如,使用默认的 AbortPolicy 拒绝策略时,会抛出 RejectedExecutionException 异常来拒绝新提交的任务。而使用 CallerRunsPolicy 策略时,新提交的任务会由提交任务的线程来执行,而不会再开启新的线程。

需要注意的是,拒绝策略的触发并不代表任务一定会被丢弃或忽视,而是指当线程池已达到最大容量且任务队列已满时,新提交的任务无法被正常处理,因此需要通过拒绝策略来决定如何处理这些无法接受的任务。

当提交任务数大于 核心线程数corePoolSize 的时候,会优先将任务放到 workQueue 阻塞队列中。当阻塞队列饱和后,会扩充线程池中线程数,直到达到 maximumPoolSize 最大线程数配置。即:当提交的任务数大于( workQueue.size() + maximumPoolSize ),就会触发线程池的拒绝策略。执行判断顺序:核心线程数 —> 阻塞队列 —> 最大线程数。

2.1 AbortPolicy(中止策略)

默认的拒绝策略,当线程池已满且任务队列也已满时,会抛出 RejectedExecutionException 异常来拒绝新提交的任务。

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    corePoolSize,
    maximumPoolSize,
    keepAliveTime,
    TimeUnit.SECONDS,
    workQueue,
    new ThreadPoolExecutor.AbortPolicy()
);

2.2 CallerRunsPolicy(调用者运行策略)

当线程池已满且任务队列也已满时,新提交的任务会由提交任务的线程来执行(调用线程自己执行),而不会开启新的线程。

2.3 DiscardPolicy(丢弃策略)

当线程池已满且任务队列也已满时,直接丢弃新提交的任务,不做任何处理。

2.4 DiscardOldestPolicy(丢弃最老策略)

当线程池已满且任务队列也已满时,丢弃最早提交的任务,然后尝试执行新提交的任务。

从源码的角度来看,这些拒绝策略都实现了接口 RejectedExecutionHandler,并实现了其中的 rejectedExecution 方法。该方法会在任务被拒绝时被调用,其中传入了被拒绝的任务和线程池。

下面是 AbortPolicy 拒绝策略的源码示例:

public class AbortPolicy implements RejectedExecutionHandler {
    public AbortPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
            " rejected from " +
            e.toString());
    }
}

其他拒绝策略的源码实现方式类似,只是在 rejectedExecution 方法中的处理逻辑不同。 当选择拒绝策略时,需要根据具体的业务场景和需求来决定使用哪种策略。比如对于一些关键的任务,可能希望通过 CallerRunsPolicy 策略让提交任务的线程自己执行,以避免任务丢失;而对于一些非关键的任务,可以选择 DiscardPolicy 策略来直接丢弃任务,从而保持线程池的稳定性。

线程池原理图示:

图片来源:https://www.cnblogs.com/yangj-Blog/p/13118694.html

下面是创建自定义线程池的代码示例:

import java.util.concurrent.*;

public class CustomerThreadPool {

//    static  class SimpleThreadFactory implements ThreadFactory {
//        public Thread newThread(Runnable r) {
//            return new Thread(r);
//        }
//    }

    static class MyAbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public MyAbortPolicy() {
        }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("任务 " + r.toString() +
                    " 拒绝 from " +
                    e.toString());
        }
    }

    public static void main(String[] args) {
        int corePoolSize = 4;
        int maximumPoolSize = 10;
        long keepAliveTime = 10000;
        TimeUnit unit = TimeUnit.MILLISECONDS;
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(10);
//        ThreadFactory threadFactory = new ThreadFactory() {
//            @Override
//            public Thread newThread(Runnable r) {
//                System.out.println("创建线程:"+r);
//                return new Thread(r);
//            }
//        };
        RejectedExecutionHandler handler = null;


        ThreadPoolExecutor myPool = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                r -> {
                    System.out.println("创建线程:" + r);
                    return new Thread(r);
                },
                new MyAbortPolicy()
        );

        for (int t = 0; t < 30; t++) {
            myPool.execute(() -> {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

在这个示例中,我们创建了一个自定义的线程池。通过设置参数来自定义线程池的行为,包括核心线程数、最大线程数、空闲线程存活时间、任务队列容量等。同时,还提供了自定义的线程工厂和饱和策略。

通过自定义这些参数和组件,可以灵活地控制线程池的行为,以适应不同的需求和场景。

需要注意的是:

图片来源:《阿里java开发手册2019 1.5》

参考

java线程池 - To_Yang - 博客园

Java线程池的拒绝策略 - 阿呆很呆非常呆 - 博客园

Java 线程池四种拒绝策略_拒绝策略是拒绝什么线程_小码A梦的博客-CSDN博客

https://www.cnblogs.com/eric-fang/p/9004020.html


  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值