zoukankan      html  css  js  c++  java
  • ThreadPoolExecutor 参数介绍 【转载】

    ThreadPoolExecutor 参数介绍

    ThreadPoolExecutor 最多可以设置 7 个参数,如下代码所示:

     public ThreadPoolExecutor(int corePoolSize,
                               int maximumPoolSize,
                               long keepAliveTime,
                               TimeUnit unit,
                               BlockingQueue<Runnable> workQueue,
                               ThreadFactory threadFactory,
                               RejectedExecutionHandler handler) {
         // 省略...
     }
    

    7 个参数代表的含义如下:

    参数 1:corePoolSize

    核心线程数,线程池中始终存活的线程数。

    参数 2:maximumPoolSize

    最大线程数,线程池中允许的最大线程数,当线程池的任务队列满了之后可以创建的最大线程数。

    参数 3:keepAliveTime

    最大线程数可以存活的时间,当线程中没有任务执行时,最大线程就会销毁一部分,最终保持核心线程数量的线程。

    参数 4:unit:

    单位是和参数 3 存活时间配合使用的,合在一起用于设定线程的存活时间 ,参数 keepAliveTime 的时间单位有以下 7 种可选:

    • TimeUnit.DAYS:天
    • TimeUnit.HOURS:小时
    • TimeUnit.MINUTES:分
    • TimeUnit.SECONDS:秒
    • TimeUnit.MILLISECONDS:毫秒
    • TimeUnit.MICROSECONDS:微妙
    • TimeUnit.NANOSECONDS:纳秒

    参数 5:workQueue

    一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全,它包含以下 7 种类型:

    • ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
    • LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
    • SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
    • PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
    • DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素。
    • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。
    • LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

    较常用的是 LinkedBlockingQueue 和 Synchronous,线程池的排队策略与 BlockingQueue 有关。

    参数 6:threadFactory

    线程工厂,主要用来创建线程,默认为正常优先级、非守护线程。

    参数 7:handler

    拒绝策略,拒绝处理任务时的策略,系统提供了 4 种可选:

    • AbortPolicy:拒绝并抛出异常。
    • CallerRunsPolicy:使用当前调用的线程来执行此任务。
    • DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务,并执行当前任务。
    • DiscardPolicy:忽略并抛弃当前任务。

    默认策略为 AbortPolicy

    线程池的执行流程

    ThreadPoolExecutor 关键节点的执行流程如下:

    • 当线程数小于核心线程数时,创建线程。
    • 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
    • 当线程数大于等于核心线程数,且任务队列已满:若线程数小于最大线程数,创建线程;若线程数等于最大线程数,抛出异常,拒绝任务。

    线程池的执行流程如下图所示:
    image.png

    线程拒绝策略

    我们来演示一下 ThreadPoolExecutor 的拒绝策略的触发,我们使用 DiscardPolicy  的拒绝策略,它会忽略并抛弃当前任务的策略,实现代码如下:

    public static void main(String[] args) {
        // 任务的具体方法
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("当前任务被执行,执行时间:" + new Date() +
                                   " 执行线程:" + Thread.currentThread().getName());
                try {
                    // 等待 1s
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        // 创建线程,线程的任务队列的长度为 1
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,
                                                               100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1),
                                                               new ThreadPoolExecutor.DiscardPolicy());
        // 添加并执行 4 个任务
        threadPool.execute(runnable);
        threadPool.execute(runnable);
        threadPool.execute(runnable);
        threadPool.execute(runnable);
    }
    

    我们创建了一个核心线程数和最大线程数都为 1 的线程池,并且给线程池的任务队列设置为 1,这样当我们有 2 个以上的任务时就会触发拒绝策略,执行的结果如下图所示:
    image.png
    从上述结果可以看出只有两个任务被正确执行了,其他多余的任务就被舍弃并忽略了。其他拒绝策略的使用类似,这里就不一一赘述了。

    自定义拒绝策略

    除了 Java 自身提供的 4 种拒绝策略之外,我们也可以自定义拒绝策略,示例代码如下:

    public static void main(String[] args) {
        // 任务的具体方法
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("当前任务被执行,执行时间:" + new Date() +
                                   " 执行线程:" + Thread.currentThread().getName());
                try {
                    // 等待 1s
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        // 创建线程,线程的任务队列的长度为 1
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,
                                                               100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1),
                                                               new RejectedExecutionHandler() {
                                                                   @Override
                                                                   public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                                                                       // 执行自定义拒绝策略的相关操作
                                                                       System.out.println("我是自定义拒绝策略~");
                                                                   }
                                                               });
        // 添加并执行 4 个任务
        threadPool.execute(runnable);
        threadPool.execute(runnable);
        threadPool.execute(runnable);
        threadPool.execute(runnable);
    }
    

    程序的执行结果如下:
    image.png

    究竟选用哪种线程池?

    经过以上的学习我们对整个线程池也有了一定的认识了,那究竟该如何选择线程池呢?

    我们来看下阿里巴巴《Java开发手册》给我们的答案:

    【强制要求】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

    说明:Executors 返回的线程池对象的弊端如下:

    1) FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

    2)CachedThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

    所以综上情况所述,我们推荐使用 ThreadPoolExecutor 的方式进行线程池的创建,因为这种创建方式更可控,并且更加明确了线程池的运行规则,可以规避一些未知的风险。

    总结

    本文我们介绍了线程池的 7 种创建方式,其中最推荐使用的是 ThreadPoolExecutor 的方式进行线程池的创建,ThreadPoolExecutor 最多可以设置 7 个参数,当然设置 5 个参数也可以正常使用,ThreadPoolExecutor 当任务过多(处理不过来)时提供了 4 种拒绝策略,当然我们也可以自定义拒绝策略,希望本文的内容能帮助到你。原创不易,觉得不错就点个赞再走吧!

    参考 & 鸣谢

    https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html

    https://www.cnblogs.com/pcheng/p/13540619.html

  • 相关阅读:
    faster-RCNN框架之rpn 较小目标检测,如果只使用rpn,并减少多个候选框
    git clone Failed to connect to 127.0.0.1 port 43213: Connection refused
    chrome不能浏览任何网页,提示配置proxy,Ubuntu
    Ubuntu16下用virtualbox 安装Windows虚拟机
    mobilenet之Depthwise +Pointwise
    联想电脑t450,t460p,t470等安装好ubuntu后启动找不到系统
    tensorflow-serving-gpu 本地编译并使用
    git克隆远程仓库的时候断电了,使用git-fetch断点续传
    java ->Servlet接口
    java ->Tomcat服务器
  • 原文地址:https://www.cnblogs.com/yy1234/p/14149622.html
Copyright © 2011-2022 走看看