【小家java】用 ThreadPoolExecutor/ThreadPoolTaskExecutor 线程池技术提高系统吞吐量(附带线程池参数详解和使用注意事项)(下)

简介: 【小家java】用 ThreadPoolExecutor/ThreadPoolTaskExecutor 线程池技术提高系统吞吐量(附带线程池参数详解和使用注意事项)(下)
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
  public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }


Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE

Executors.newSingleThreadExecutor(); //创建容量为1的缓冲池

Executors.newFixedThreadPool(int); //创建固定容量大小的缓冲池


ThreadPoolExecutor 的继承关系如下


Executor->ExecutorService->AbstractExecutorService->ThreadPoolExecutor


其中有一个构造方法:


public ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue,
                           RejectedExecutionHandler handler) {
     this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
          Executors.defaultThreadFactory(), handler);
 }
 发现我们可以指定workQueue和handler。当然还有其余的构造函数,有类似的效果


线程池构造函数7大参数解释:

  corePoolSize:核心线程数。


  maximumPoolSize:最大线程数。表明线程中最多能够创建的线程数量。


  keepAliveTime:空闲的线程保留的时间。


  unit:空闲线程的保留时间单位。

  BlockingQueue workQueue:用于保存等待执行的任务的阻塞队列。可以选择以下几个阻塞队列。


1、ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。


2、LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列


3、SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。


4、PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

threadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程做些更有意义的事情,比如设置daemon和优先级等等

handler:饱和策略处理器。默认提供的4中策略上面已经有解释了


强烈建议程序员使用较为方便的 Executors 工厂方法 Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景预定义了设置。



但是,但是,但是。。。用fix是有坑的。详情请见我这篇博文(在生产环境的一个活生生的血案):


另外,此处我说一下ThreadPoolTaskExecutor:


ThreadPoolTaskExecutor是一个spring的线程池技术,其实,它的实现方式完全是使用ThreadPoolExecutor进行实现(有点类似于装饰者模式。当然Spring提供的功能更加强大些,因为还有定时调度功能)。


三、如何设置线程池的参数:


系统默认值

corePoolSize=1

queueCapacity=Integer.MAX_VALUE

maxPoolSize=Integer.MAX_VALUE

keepAliveTime=60s

allowCoreThreadTimeout=false

rejectedExecutionHandler=AbortPolicy()


那我们如何来设置呢?需要根据几个值来决定


tasks :每秒的任务数,假设为500~1000

taskcost:每个任务花费时间,假设为0.1s

responsetime:系统允许容忍的最大响应时间,假设为1s

做几个计算

corePoolSize = 每秒需要多少个线程处理?


threadcount = tasks/(1/taskcost) =tasks*taskcout = (500~1000)*0.1 = 50~100 个线程。corePoolSize设置应该大于50

根据8020原则,如果80%的每秒任务数小于800,那么corePoolSize设置为80即可

queueCapacity = (coreSizePool/taskcost)*responsetime

计算可得 queueCapacity = 80/1 = 80。意思是队列里的线程可以等待1s,超过了的需要新开线程来执行

切记不能设置为Integer.MAX_VALUE,这样队列会很大,线程数只会保持在corePoolSize大小,当任务陡增时,不能新开线程来执行,响应时间会随之陡增。

maxPoolSize = (max(tasks)- queueCapacity)/(1/taskcost)

计算可得 maxPoolSize = (1000-80)/10 = 92

(最大任务数-队列容量)/每个线程每秒处理能力 = 最大线程数


rejectedExecutionHandler:根据具体情况来决定,任务不重要可丢弃,任务重要则要利用一些缓冲机制来处理


keepAliveTime和allowCoreThreadTimeout采用默认通常能满足

以上都是理想值,实际情况下要根据机器性能来决定。如果在未达到最大线程数的情况机器cpu load已经满了,则需要通过升级硬件(呵呵)和优化代码,降低taskcost来处理。


JDK1.5 的线程池由 Executor 框架提供。 Executor 框架将处理请求任务的提交和它的执行解耦。可以制定执行策略。在线程池中执行线程可以重用已经存在的线程,而不是创建新的线程,可以在处理多请求时抵消线程创建、消亡产生的开销。如果线程池过大,会导致内存的高使用量,还可能耗尽资源。如果过小,会由于存在很多的处理器资源未工作,对吞吐量造成损失。


如何合理配置线程池大小,一般需要根据任务的类型来配置线程池大小:

 1、如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 NCPU+1(比如是4核心 就配置为5)

 2、如果是IO密集型任务,参考值可以设置为2*NCPU

 当然,这只是一个参考值,具体的设置还需要根据实际情况进行调整,比如可以先将线程池大小设置为参考值,再观察任务运行情况和系统负载、资源利用率来进行适当调整。


3、使用场景



1、当你的任务是非必要的时候。比如记录操作日志、通知第三方服务非必要信息等,可以使用线程池处理非阻塞任务

2、当你的任务非常耗时时候,可以采用线程池技术

3、当请求并发很高时,可以采用线程池技术优化处理


可以通过Executors静态工厂构建线程池,但一般不建议这样使用。


提醒:能够用线程池的时候,不要自己的去new线程start,在高并发环境下,系统资源是宝贵的,需要节约资源才能提高可用性。


小彩蛋

Executors工具类给我们提供了不少快捷创建线程池的方法,虽然我们不推荐使用。但是里面有个方法我觉得还是不错的:Executors.newSingleThreadExecutor() 如果把这个当作全局线程池,可以很好实现异步,并且还能保证任务的顺序执行,进而达到消峰的效果:

        private static final ExecutorService executorService = Executors.newSingleThreadExecutor();
    private static AtomicInteger num = new AtomicInteger();
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            executorService.execute(() -> {
                num.getAndIncrement();
                System.out.println(Thread.currentThread().getName() + "-->>>>" + num);
            });
        }
        executorService.shutdown();
    }
输出:
pool-1-thread-1-->>>>1
pool-1-thread-1-->>>>2
pool-1-thread-1-->>>>3
pool-1-thread-1-->>>>4
pool-1-thread-1-->>>>5
pool-1-thread-1-->>>>6
pool-1-thread-1-->>>>7
pool-1-thread-1-->>>>8
pool-1-thread-1-->>>>9
pool-1-thread-1-->>>>10
pool-1-thread-1-->>>>11
pool-1-thread-1-->>>>12
pool-1-thread-1-->>>>13
pool-1-thread-1-->>>>14
pool-1-thread-1-->>>>15
pool-1-thread-1-->>>>16
pool-1-thread-1-->>>>17
pool-1-thread-1-->>>>18
pool-1-thread-1-->>>>19
pool-1-thread-1-->>>>20


我们发现pool只有一个,且thread也只有一个,而且任务都是顺序执行的。当我们用Fiexed的话,也是能够达到顺序执行的效果的。因为内部的阻塞队列是FIFO的实现:


    private static final ExecutorService executorService = Executors.newFixedThreadPool(5);
    private static AtomicInteger num = new AtomicInteger();
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            executorService.execute(() -> {
                System.out.println(Thread.currentThread().getName() + "-->>>>" + num.getAndIncrement());
            });
        }
        executorService.shutdown();
    }
输出:
pool-1-thread-2-->>>>0
pool-1-thread-1-->>>>1
pool-1-thread-2-->>>>2
pool-1-thread-1-->>>>3
pool-1-thread-2-->>>>4
pool-1-thread-1-->>>>5
pool-1-thread-2-->>>>6
pool-1-thread-1-->>>>7
pool-1-thread-2-->>>>8
pool-1-thread-1-->>>>9
pool-1-thread-2-->>>>10
pool-1-thread-1-->>>>11
pool-1-thread-2-->>>>12
pool-1-thread-1-->>>>13
pool-1-thread-2-->>>>14
pool-1-thread-1-->>>>15
pool-1-thread-2-->>>>16
pool-1-thread-4-->>>>17
pool-1-thread-3-->>>>18
pool-1-thread-5-->>>>19
相关文章
|
1月前
|
消息中间件 Java Kafka
Java 事件驱动架构设计实战与 Kafka 生态系统组件实操全流程指南
本指南详解Java事件驱动架构与Kafka生态实操,涵盖环境搭建、事件模型定义、生产者与消费者实现、事件测试及高级特性,助你快速构建高可扩展分布式系统。
145 7
|
2月前
|
存储 Java 数据库连接
java 初学者必看的系统知识结构图详解
本文详解Java知识结构图,涵盖Java语言基础、JVM原理、集合框架、并发编程、网络通信及主流框架(如Spring Boot、MyBatis),并结合学生信息管理系统实例,帮助初学者构建完整知识体系,提升实战开发能力。
76 0
|
1月前
|
安全 Oracle Java
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
159 0
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
|
3月前
|
NoSQL Java Shell
2025服务端java搭建篇:蜻蜓I即时通讯系统私有化部署深度指南-优雅草卓伊凡|麻子|贝贝
2025服务端java搭建篇:蜻蜓I即时通讯系统私有化部署深度指南-优雅草卓伊凡|麻子|贝贝
141 8
2025服务端java搭建篇:蜻蜓I即时通讯系统私有化部署深度指南-优雅草卓伊凡|麻子|贝贝
|
4月前
|
JavaScript Java 关系型数据库
家政系统源码,java版本
这是一款基于SpringBoot后端框架、MySQL数据库及Uniapp移动端开发的家政预约上门服务系统。
146 6
家政系统源码,java版本
|
3月前
|
NoSQL Java Redis
推荐一款好用的开源免费Java CMS内容管理站群系统
Java开源内容管理系统(JProcms),基于SpringCloud、SpringBoot、MyBatisPlus、Vue3等技术构建,采用Apache-2.0协议,支持免费商用。系统具备自定义字段存储与可视化设计、API制作网站群页面等功能,强调简单灵活的设计理念,降低二次开发成本。支持多种数据库、消息队列和认证方式,提供SaaS多租户、动态权限菜单、工作流配置等强大功能,同时集成阿里云、腾讯云服务,适用于高效建站与内容管理。
653 4
|
2月前
|
存储 Java 关系型数据库
Java Swing 开发的五星级酒店客房预订与管理系统源码
本文介绍了基于Java Swing的酒店管理系统开发方案。系统采用Java Swing构建GUI界面,结合MySQL数据库,实现预订管理、前台服务、客房管理、客户关系维护等功能模块。文章详细展示了登录界面、开房操作等核心功能的代码实现,包括数据验证和业务逻辑处理。该系统具有跨平台性,能有效提升酒店运营效率,为开发者提供GUI设计和数据库开发的实践案例。技术方案涵盖IntelliJ IDEA开发环境、Jform Designer插件辅助设计等工具链,适合中小型酒店管理需求。
129 0
|
3月前
|
Java 调度 流计算
基于Java 17 + Spring Boot 3.2 + Flink 1.18的智慧实验室管理系统核心代码
这是一套基于Java 17、Spring Boot 3.2和Flink 1.18开发的智慧实验室管理系统核心代码。系统涵盖多协议设备接入(支持OPC UA、MQTT等12种工业协议)、实时异常检测(Flink流处理引擎实现设备状态监控)、强化学习调度(Q-Learning算法优化资源分配)、三维可视化(JavaFX与WebGL渲染实验室空间)、微服务架构(Spring Cloud构建分布式体系)及数据湖建设(Spark构建实验室数据仓库)。实际应用中,该系统显著提升了设备调度效率(响应时间从46分钟降至9秒)、设备利用率(从41%提升至89%),并大幅减少实验准备时间和维护成本。
246 0
|
3月前
|
监控 安全 Java
现代 Java 技术开发宠物管理系统实操案例解析
本文通过一个宠物管理系统项目,深入讲解Java中的封装、继承和多态特性。系统基于Java 8+开发,结合Stream API与Lambda表达式,展示现代Java技术的实际应用。封装部分使用Record类简化数据模型,继承设计通过抽象类与接口实现代码复用,多态则通过统一接口处理不同宠物行为。案例涵盖模板方法模式、策略模式等设计思想,并提供最佳实践建议,解决常见问题如继承滥用和类型转换异常。资源地址:[https://pan.quark.cn/s/14fcf913bae6](https://pan.quark.cn/s/14fcf913bae6)。
100 0
|
传感器 分布式计算 安全
Java 大视界 -- Java 大数据在智能安防入侵检测系统中的多源数据融合与分析技术(171)
本文围绕 Java 大数据在智能安防入侵检测系统中的应用展开,剖析系统现状与挑战,阐释多源数据融合及分析技术,结合案例与代码给出实操方案,提升入侵检测效能。

热门文章

最新文章