百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

线程池调优之动态参数配置(线程池参数设置)

wxin55 2024-11-07 13:12 10 浏览 0 评论

前言

线程池的核心参数配置在网上有一大堆的文章介绍,这次结合个人理解写一篇文章记录一下,以便加深印象和后续查阅。
复制代码

线程池配置参数

corePoolSize:线程池核心线程数 maximumPoolSize:线程池最大线程数 keepAliveTime:允许线程空闲时间(对非核心工作线程的回收) TimeUnit:线程空闲时间单位 workQueue:线程队列(当核心线程数满了,新的任务就会放入这个队列中) threadFactory:线程工厂(用于创建工作线程,自定义线程工厂可以指定线程名称) handler:线程池拒绝策略(当线程队列满了且最大线程数也满了,就会执行任务拒绝策略,默认有4种) allowCoreThreadTimeOut:控制核心工作线程是否需要被回收

常规线程池参数配置

- 首先提问一个面试题:现有1000个任务,10台服务器,每台机器都是4核,在任务不丢弃情况下,线程池参数该怎么配置最合理呢?
- 把这个问题拆分一下,1000个任务,10台机器,那么每台机器就负责100个任务(常规轮训负载均衡模式,不考虑其他额外情况),每台机器都是4核,那么就可以设置核心线程数和最大线程数为4,线程队列大小为96即可。
- 当然也可以把核心和最大线程数设置为5(n+1)个,线程队列大小为95,这样是为了防止线程偶尔由于页缺失故障或者其他原因暂停,出多来的一个线程也能确保CPU的调度时钟周期不会被浪费,相当于备用线程。
复制代码
  • 如果任务是CPU密集型配置:工作线程 = cpu核心数 + 1;
  • 如果任务是IO密集型场景:工作线程 = cpu核心数 * 2;
  • 所以上面例子中就是基于CPU密集型任务配置线程池。而且网上大部分文章描述线程池配置也是基于这两点来分析的。
  • 可惜理想很丰满,现实很骨感。在实际工作场景中,其实没那么容易区分线程中执行的任务是CPU密集还是IO密集,而且服务器上还会有其他应用线程抢占CPU资源,就算还有一些其他的公式计算配置线程池参数,那也是基于理想场景情况下进行配置的,所以上述配置更多的还是应用于面试中。

动态配置线程池参数

  • 上述中既然不能一次定义适配所有场景的线程池参数,那么如果可以根据不同业务场景动态配置线程池参数,通过人工干预介入来适配大部分场景也行的
  • 正好在JDK的自定义线程池ThreadPoolExecutor里,提供了动态扩展线程池核心参数的方法
  • 可以在运行期间的线程池使用此方法可以覆盖原来配置的值:
ThreadPoolExecutor线程池提供了5种配置参数可供动态更新:核心线程池,最大线程数,线程工厂,线程空闲时间,拒绝策略。
复制代码
  • 这里主要讨论的是核心线程池和最大线程池两种参数配置:
/**
 *
 * @Author: ZRH
 * @Date: 2021/10/08 17:30
 */
@Slf4j
public class ExecutorTest {

    public static void main (String[] args) throws Exception {
        final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
                3,
                30,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(7),
                new ThreadPoolExecutor.DiscardPolicy());
        for (int i = 0; i < 10; i++) {
            threadPoolExecutor.execute(() -> {
                try {
                    logExecutorInfo(threadPoolExecutor);
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                }
            });
        }
        logExecutorInfo(threadPoolExecutor);
        threadPoolExecutor.setCorePoolSize(5);
        threadPoolExecutor.setMaximumPoolSize(5);
        logExecutorInfo(threadPoolExecutor);
        Thread.currentThread().join();
    }

    private static void logExecutorInfo (ThreadPoolExecutor executor) {
        log.info("线程池核心线程数=" + executor.getCorePoolSize() +
                ", 线程池最大线程数=" + executor.getMaximumPoolSize() +
                ", 线程池队列剩余任务=" + executor.getQueue().size() +
                ", 线程池活跃线程数=" + executor.getActiveCount() +
                ", 线程池任务完成数" + executor.getCompletedTaskCount());
    }
}
复制代码
  • 看执行结果:刚开始线程池里核心线程数2个、最大线程数3个、剩下7个队列。活跃的线程也只有3个。
  • 然后更改核心线程和最大线程数为5后,线程池里对应的核心线程数和最大线程数也增加至5个,活跃的工作线程也是5个。说明更改配置成功。
  • 注:更新线程池参数时,核心线程数不能超过最大线程数配置。否则配置最后不会生效。
public static void main (String[] args) throws Exception {
    final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
            3,
            30,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(7),
            new ThreadPoolExecutor.DiscardPolicy());
    for (int i = 0; i < 10; i++) {
        threadPoolExecutor.execute(() -> {
            try {
                logExecutorInfo(threadPoolExecutor);
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
        });
    }
    logExecutorInfo(threadPoolExecutor);
    threadPoolExecutor.setCorePoolSize(5);
    // threadPoolExecutor.setMaximumPoolSize(5);
    logExecutorInfo(threadPoolExecutor);
    Thread.currentThread().join();
}
复制代码
  • 上图中把核心线程数更新为5,最大线程数不改动任为3。最后看执行结果,最终的活跃线程还是3个,说明配置没有生效,具体源码在ThreadPoolExecutor类的getTask()方法里,感兴趣的同学可以去看一下...

动态更新线程队列

  • ThreadPoolExecutor线程池并没有动态配置线程池队列大小的方法
  • 想自己操作一下也是很简单的,只需要自定义实现一个队列,可以直接把LinkedBlockingQueue复制一份,并把capacity参数设定为可更改
public static void main (String[] args) throws Exception {
    final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
            3,
            30,
            TimeUnit.SECONDS,
            new CustomLinkedBlockingQueue<>(7),
            new ThreadPoolExecutor.DiscardPolicy());
    for (int i = 0; i < 10; i++) {
        threadPoolExecutor.execute(() -> {
            try {
                logExecutorInfo(threadPoolExecutor);
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
        });
    }
    logExecutorInfo(threadPoolExecutor);
    threadPoolExecutor.setCorePoolSize(5);
    threadPoolExecutor.setMaximumPoolSize(5);
    CustomLinkedBlockingQueue queue = (CustomLinkedBlockingQueue)threadPoolExecutor.getQueue();
    queue.setCapacity(10);
    for (int i = 0; i < 10; i++) {
        threadPoolExecutor.execute(() -> {
            try {
                logExecutorInfo(threadPoolExecutor);
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
        });
    }
    Thread.currentThread().join();
}
复制代码
  • 看结果,后续添加的任务会放入队列中,并且队列大小也超过第一次设置大小,说明配置成功


作者:超级爽朗的郑
链接:https://juejin.cn/post/7016639323369898015
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


相关推荐

ES6中 Promise的使用场景?(es6promise用法例子)

一、介绍Promise,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大在以往我们如果处理多层异步操作,我们往往会像下面那样编写我们的代码doSomething(f...

JavaScript 对 Promise 并发的处理方法

Promise对象代表一个未来的值,它有三种状态:pending待定,这是Promise的初始状态,它可能成功,也可能失败,前途未卜fulfilled已完成,这是一种成功的状态,此时可以获取...

Promise的九大方法(promise的实例方法)

1、promise.resolv静态方法Promise.resolve(value)可以认为是newPromise方法的语法糖,比如Promise.resolve(42)可以认为是以下代码的语...

360前端一面~面试题解析(360前端开发面试题)

1.组件库按需加载怎么做的,具体打包配了什么-按需加载实现:借助打包工具(如Webpack的require.context或ES模块动态导入),在使用组件时才引入对应的代码。例如在V...

前端面试-Promise 的 finally 怎么实现的?如何在工作中使用?

Promise的finally方法是一个非常有用的工具,它无论Promise是成功(fulfilled)还是失败(rejected)都会执行,且不改变Promise的最终结果。它的实现原...

最简单手写Promise,30行代码理解Promise核心原理和发布订阅模式

看了全网手写Promise的,大部分对于新手还是比较难理解的,其中几个比较难的点:状态还未改变时通过发布订阅模式去收集事件实例化的时候通过调用构造函数里传出来的方法去修改类里面的状态,这个叫Re...

前端分享-Promise可以中途取消啦(promise可以取消吗)

传统Promise就像一台需要手动组装的设备,每次使用都要重新接线。而Promise.withResolvers的出现,相当于给开发者发了一个智能遥控器,可以随时随地控制异步操作。它解决了三大...

手写 Promise(手写输入法 中文)

前言都2020年了,Promise大家肯定都在用了,但是估计很多人对其原理还是一知半解,今天就让我们一起实现一个符合PromiseA+规范的Promise。附PromiseA+规范地址...

什么是 Promise.allSettled()!新手老手都要会?

Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的pr...

前端面试-关于Promise解析与高频面试题示范

Promise是啥,直接上图:Promise就是处理异步函数的API,它可以包裹一个异步函数,在异步函数完成时抛出完成状态,让代码结束远古时无限回掉的窘境。配合async/await语法糖,可...

宇宙厂:为什么前端离不开 Promise.withResolvers() ?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发。1.为什么需要Promise.with...

Promise 新增了一个超实用的 API!

在JavaScript的世界里,Promise一直是处理异步操作的神器。而现在,随着ES2025的发布,Promise又迎来了一个超实用的新成员——Promise.try()!这个新方法简...

一次搞懂 Promise 异步处理(promise 异步顺序执行)

PromisePromise就像这个词的表面意识一样,表示一种承诺、许诺,会在后面给出一个结果,成功或者失败。现在已经成为了主流的异步编程的操作方式,写进了标准里面。状态Promise有且仅有...

Promise 核心机制详解(promise机制的实现原理)

一、Promise的核心状态机Promise本质上是一个状态机,其行为由内部状态严格管控。每个Promise实例在创建时处于Pending(等待)状态,此时异步操作尚未完成。当异步操作成功...

javascript——Promise(js实现promise)

1.PromiseES6开始支持,Promise对象用于一个异步操作的最终完成(包括成功和失败)及结果值的表示。简单说就是处理异步请求的。之所以叫Promise,就是我承诺,如果成功则怎么处理,失败怎...

取消回复欢迎 发表评论: