Java线程池的技巧及应用小结

AI智能体协作:构建更精准的公司破产预测模型 公司破产预测对于金融机构、投资者和监管部门等具有至关重要的意义。准确的破产预测可以帮助金融机构提前做好风险防范,避免贷款损失;投资者可以根据预测结果调整投资组合,降低投资风险;监管部门可以及时发现潜在的金融风险,采取相应的监管措施。然而,传统的公司破产预测模型往往存在精度不高的问题,无法满足实际需求。本研究的目的是利用AI智能体协作的方式,构建一个更精准的公司破产预测模型。通过将多个AI智能体的优势进行整合,充分挖掘公司财务数据、市场数据等多源数据中的信息,提高预测的准确性和可靠性。 阅读详情

title: Java线程池的技巧及应用小结
date: 2018-07-05 10:11:35
categories:

  • 工作
    tags:
  • Java

近来重温了一些Java方面关于线程池使用的书籍及使用场景,使用后感觉有必要整理归纳一下。

引入:我们使用线程的时候就去创建一个线程,这样实现起来非常简便。但是会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

一、基础且必须知道的

线程池主要是围绕着java.util.concurrent.ThreadPoolExecutor展开的,如果要透彻地了解Java中的线程池,必须先了解这个类。

ThreadPoolExecutor继承自AbstractExecutorService类,并提供了四个构造器。但是,仔细观察每个构造器的源码具体实现,发现前面三个构造器其实是调用的第四个构造器进行的初始化工作。

那我们直接看第四个构造器的具体功能:

undefined
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) ;

这个构造器的功能自然是创建一个线程池(Creates a new {@code ThreadPoolExecutor} with the given initial parameters)

  1. 具体参数的含义如下:
  • corePoolSize:
    the number of threads to keep in the pool,即常驻在线程池中的线程数目。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务。当然也可以使用"预热"的方法:prestartCoreThread() 和 prestartAllCoreThreads(),前者是启动corePoolSize个线程,后者是启动一个线程。当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
  • maximumPoolSize
    表示在线程池中最多能创建多少个线程。
  • keepAliveTime
    表示线程没有任务执行时最多保持多久时间会终止。
  • unit
    参数keepAliveTime对应的时间单位,即TimeUnit类中有7种静态属性,eg:TimeUnit.SECONDS
  • workQueue
    阻塞队列,用来存储等待执行的任务。该参数的选择会对线程池运行过程产生重大影响。具体影响在下文谈到。
一般有:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue
  • threadFactory
    线程工厂,主要用来创建线程。
  • handler:表示当拒绝处理任务时的策略,有以下四种取值,具体有什么不同也会在下文详细描述。
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 
  1. 在组织结构上

ThreadPoolExecutor类继承自AbstractExecutorService,AbstractExecutorService实现了ExecutorService接口,ExecutorService接口又继承自Executor接口。
顶层接口:Executor,只有execute()方法,用来定义执行任务的方法。

void execute(Runnable command);

二级接口:ExecutorService,定义了 shutdown()、submit()、invokeAll()等主要方法。
三级实现类:AbstractExecutorService,实现了ExecutorService接口中的方法。
四级实现类:ThreadPoolExecutor进一步扩展功能,主要实现了execute()方法。同时多了些状态获取,控制的方法。并且,定义了四种拒绝处理任务时的策略。

  1. execute()方法 VS submit()方法

execute()方法在Executor中声明,在ThreadPoolExecutor进行实现,通过这个方法可以向线程池提交一个任务,交由线程池去执行。没有返回值。

​  submit()方法中调用了execute()方法,只是在返回值方面,使用了Future<T>

  1. 整体的运行过程__(重要!)__
  • 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;
  • 如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
  • 如果当前线程池中的线程数目达到maximumPoolSize,则会__采取任务拒绝策略__进行处理;
  • 如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。

二、任务缓存队列及排队策略

  1. 使用直接提交策略,即__SynchronousQueue__。

SynchronousQueue是无界的,也就是说他存数任务的能力是没有限制的,但是由于该Queue本身的特性,在某次添加元素后必须等待其他线程取走后才能继续添加。

  1. 使用无界队列策略,即__LinkedBlockingQueue__。

在达到corePoolSize线程数目之后,__因为是无界,所以任务总是可以加入队列的,因而永远也不会触发产生新的线程!__corePoolSize大小的线程数会一直运行,忙完当前的,就从队列中拿任务开始运行。如果当前线程数处理不过来,队列会出现OOM,内存溢出!

  1. 有界队列,使用__ArrayBlockingQueue__。

顾名思义,因为有界,达到corePoolSize线程数目之后,可以创建新的线程。并且,线程数目达到maximumPoolSize,之后采用拒绝策略。故而可以防止资源耗尽的情况发生。

三、拒绝策略

在ThreadPoolExecutor中定义。

四种策略

​ 1、AbortPolicy 丢弃任务并抛出RejectedExecutionException异常(常用)。

2、DiscardPolicy 丢弃当前将要加入队列的任务本身,无异常抛出。

​ 3、DiscardOldestPolicy 丢弃任务队列中最旧任务,随后重新尝试执行任务。

​ 4、CallerRunsPolicy 不进入线程池执行,任务将有调用者线程去执行。

四、常见应用场景

场景

描述: 前端一次请求要求3s内返回结果,否则走打底逻辑。很多情况下前端需要的数据是由多个数据源的数据组成,且彼此间没有依赖关系。
解决思路
如果这些数据采用串行获取,大概率出现超时,所以对于没有依赖关系的数据,则可以并发执行访问。
具体执行时,可以采用,传递response引用的方式,多个数据源实例向response中放值。
main-idea:将所有submit线程的对应future,放入公共的list中,然后对该list的每个future进行get获取结果&设置超时时间。
具体步骤

第一步 规范数据源访问

定义接口: BaseBuilder<R, S>,同时定义具体方法 build方法:build(R request, S response);

数据源访问的类 用类ABuilder、BBuilder和CBuilder表示。

在本次请求中,所有需要访问数据源的类ABuilder、BBuilder和CBuilder都需要实现BaseBuilder接口和build方法。

例如:

public class ABuilder implements BaseBuilder<Request, Response> {
    @Override
    public void build(Request request, Response response) {
        try {
            // 获取A数据源的数据
            A aResult = aservice.getA(request);
            response.setA(aResult);
        } catch(Exception e){
            // 异常处理
        } finally {
            // 统计信息
        }
    }
}
第二步 定义线程池信息

定义类 BaseThreadPoolExecutor<R, S>

定义成员变量:线程池exec

private final ExecutorService exec = new ThreadPoolExecutor(32, 128, 200, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.AbortPolicy());

定义新增线程的方法:

public void addPool(List<Future> futureList,
                        final BaseBuilder<R, S> baseBuild, final R r, final S s) {
​
        Future<Void> future = exec.submit(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                baseBuild.build(r, s);
                return ;
            }
        });
        futureList.add(future);
}

获取数据源结果的方法:

/**
* 获取Future结果
* @param futureList
* @param expireTime 总超时时间 
*/
public void getFutureList(List<Future> futureList, long expireTime) {
        for (Future ft : futureList) {
            Long start = System.nanoTime();
            try {
                ft.get(expireTime, TimeUnit.MILLISECONDS); //阻塞,线程等待时间
                Long timeUse = System.nanoTime() - start;
                expireTime = expireTime - timeUse;
                if (expireTime < 0){
                    throw new XXXException("time out");
                }
            } catch (Exception e) {
                ft.cancel(false);
                LoggerUtils.error(logger, e, "");
                break;
            }
        }
}
第三步 调用

实例化BaseThreadPoolExecutor,并注入

@Resource
private BaseThreadPoolExecutor<Request, Response> baseThreadPoolExecutor; //成员变量

在某个方法里开启多线程逻辑

List<Future> futureList = Lists.newArrayList();
baseThreadPoolExecutor.addPool(futureList, ABuilder, context, response);
baseThreadPoolExecutor.addPool(futureList, BBuilder, context, response);
baseThreadPoolExecutor.addPool(futureList, CBuilder, context, response);
baseThreadPoolExecutor.getFutureList(futureList, 3000000L); //超时时间为3000000s。

此时response中就有我们需要的三个数据源A、B、C的数据了。当然,如果没有就超时或者报错了。

note:共同对response操作时,最好不要对同一个成员变量操作,以防止线程安全问题的出现。

五、如何合理配置线程池的大小

一般需要根据任务的类型来配置线程池大小,目标是整体上保证CPU不要闲着:

如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 CPU+1

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

当然,这只是一个参考值,是具体情况而定。

致谢-参考
Springboot + vue 实现导出word Springboot + vue 实现导出word 后端代码 使用word先创建好已知的模板 使用word工具另存为.xml格式的文件 使用freemarker ftl模板 将修改后的xml文件复制到开发工具中,将文件后缀修改为ftl文件,并且将里面的动态值使用${}包裹例如:${zyjn} word工具类 /*** * * Project Name:maventest * <p>生成word工具类<br> * * @ClassName: WordUtil * 阅读详情

相关推荐

基于STM32智能震动报警器震动检测声光报警系统设计

*单片机设计介绍,基于STM32智能震动报警器震动检测声光报警系统设计。

2201_75940997的博客 2149

ThreadPoolExecutor线程池的使用详解

java线程池ThreadPoolExecutor类使用详解

qq_26784075的博客 3431

基于STM32F103C8T6的红外循迹避障小车

编程思路:控制核心板IO口的电平(即电机驱动模块的AIN1,AIN2,BIN1,BIN2)从而控制AO1、AO2、BO1、BO2的电平高低,进而控制电机使其正转、反转、不转。由于单纯地拉高拉低电平只能让电机在转与不转的状态切换,故决定利用TIM4的两个通道分别产生两路PWM信号来控制电机速度,四个电机两个一组,同一侧的两个连接在同一个PWM上。(#include "dianji.h"文件中同时写了对循迹模块引脚的初始化)

weixin_48743503的博客 1万+

ThreadPoolExecutor 源代码解析(base jdk1.8)

ThreadPoolExecutor 是java线程池的默认实现。本文从源代码的角度来解析线程池,后续会出一个系列的源代码解析。1.线程池初始化下面是线程池最基础的初始化函数public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, ...

wx_vampire的专栏 760

当Flowable遇上AI:智能流程决策的未来探索

本文探讨了Flowable工作流引擎与AI技术的融合,构建智能流程决策系统的前沿实践。通过动态流程控制、机器学习优化表达式、自然语言生成规则等创新方法,显著提升业务流程的自动化水平和决策效率。重点解析了AI如何赋能Flowable核心引擎,实现从静态规则到动态智能的跨越式发展。

weixin_29271053的博客 391

基于DE2-115红外解码实验

基于DE2-115红外解码实验   一、   功能描述 在同一个遥控电路中通常要使用实现不同的遥控功能或区分不同的机器类型, 这样就要求信号按一定的编码传送,编码则会由编码芯片或电路完成。对应于编码芯片通常会有相配对的解码芯片或包含解码模块的应用芯片。在实际的产品设计或业余电子制作中,编码芯片并一定能完成我们要求的功能,这时我们就需要了解所使用的编码芯片到底是如何编码的。只有知道编码方式,我...

乘风的博客 2513

ThreadPoolExecutor详解

一、ThreadPoolExecutor类讲解 1、线程池状态: 五种状态: 线程池的状态 说明 RUNNING 允许提交并处理任务 SHUTDOWN 不允许提交新的任务,但是会处理完已提交的任务 STOP 不允许提交新的任务,也不会处理阻塞队列中未执行的任务, 并设置正在执行的线程的中断标志位 TIDYING .

赶路人儿 11万+

QT应用编程: 编写低功耗BLE蓝牙调试助手(Android系统APP)

一、环境介绍 QT版本: 5.12.6 编译环境: win10 64位 目标系统: Android 完整工程源码下载地址(包含APK文件):https://download.youkuaiyun.com/download/xiaolong1126626497/19051696 二、功能介绍 本软件是一款BLE蓝牙串口调试助手,支持常规的发送和接收调试,BLE是低功耗类型蓝牙,在智能家居、物联网领域使用较多。BLE低功耗蓝牙一般不能直接使用手机进行连接,手机蓝牙默认都是工作在传统蓝牙模式,无法直接连接BL..

7658

线程池ThreadPoolExecutor详解(整理详细)

线程池

trusause的博客 2万+

Android Activity面试题最全解析(5万字长文)

Intent是Android中用于启动活动、服务或广播的一种消息对象。它包含了一系列键值对数据,用于描述操作的目的以及需要传递的信息。IntentString:字符串intfloatlong:基本数据类型Parcelable:实现了Parcelable接口的对象:实现了接口的对象示例代码如下:Java深色版本1// 创建Intent并传递数据");67// 启动新的Activity是一个XML标签,用于定义ActivityService或可以响应的Intent类型。它包含了。

大模型大数据攻城狮的专栏 801

十八万字《python从零到精通教程》第二版,贴心保姆教你从零变大神,学不会找我

文章目录一.pycharm下载安装二.python下载安装三.pycharm上配置python四.配置镜像源让你下载嗖嗖的快4.1pycharm内部配置4.2手动添加镜像源4.3永久配置镜像源五.插件安装(比如汉化?)5.1自动补码神器第一款5.2 自动补码神器第二款5.2汉化pycharm5.3其它插件六.美女背景七.自定义脚本开头八、这个前言一定要看九、python入门十、python缩进十一、Python注释1.单行注释2.多行注释十二、Python 变量1.变量定义理解2. 变量名命名3. 分配多个

全栈川川 7万+

超详细的线程池使用解析

前言 ​ Java 中线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。合理的使用线程池可以带来多个好处: ​ (1)降低资源消耗。通过重复利用已创建的线程降低线程在创建和销毁时造成的消耗。 ​ (2)提高响应速度。当处理执行任务时,任务可以不需要等待线程的创建就能立刻执行。 ​ (3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。 1、线程池的实现原理 线程池的处理

wdj_yyds的博客 4445

【WRF安装第三期(Ubuntu)】搭建WRF编译所需系统-第三方依赖库安装

WRF安装第三期:搭建WRF编译所需系统-第三方依赖库安装

WW、forever的博客 3137

Java线程池详细介绍与使用

文章目录前言一、线程池基础1、什么是线程池2、为什么使用线程池3、线程池有那些优势二、线程池使用1、Java内置线程池:ThreadPoolExecutor2、通过Executor工厂类中的静态方法获取线程池对象第一种、通过newCachedThreadPool获取线程池对象第二种、通过newFixedThreadPool获取线程池对象第三种、通过newSingleThreadExecutor获取线程池对象关闭线程方法:shutdown和shutdownNow的区别三种创建线程池的区别3、Java内置线程池

weixin_47316183的博客 1万+

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Java线程池知识点小结

查看调用发现,原来后台有一个处理字符串并将内容写入到文本文件的操作,综合来看相当于一个计算型任务,由于这个任务不是很经常出现,所以开发者就设置两个线程,并且为了让任务能够正确完成,拒绝策略也是使用。上文提到使用execute提交无返回值的任务,这样异常就会被感知,但还需要注意的是频繁的抛出异常会让线程消亡,导致线程池每次执行新任务时回去创建新的线程。所以我们建议,对于线程池的中的任务尽可能不要用异常来处理逻辑,对于可以预见的异常,我们建议手动处理返回,避免线程销毁再创建的开销。的线程数继续处理应急任务呢?

sc35262的博客 915

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Java并发编程与技术内幕:线程池深入理解

          林炳文Evankaka原创作品。转载请注明出处http://blog.youkuaiyun.com/evankaka          摘要: 本文主要讲了Java当中的线程池的使用方法、注意事项及其实现源码实现原理,并辅以实例加以说明,对加深Java线程池的理解有很大的帮助。          首先,讲讲什么是线程池?照笔者的简单理解,其实就是一组线程实时处理休眠状态,等待唤醒执行。...

Evankaka的专栏 5万+

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

java线程池小结

1.线程池相关的类 1.Executor接口,将任务提交与任务的细节解耦开。Executor接口只有一个execute方法 public interface Executor { /** * Executes the given command at some time in the future. The command * may execute in a ne...

bitcarmanlee的博客 417

Java线程池ThreadPoolExecutor详细介绍与使用

Java线程池ThreadPoolExecutor详细介绍与使用

qq_27480007的博客 1万+

java线程池之小结

先看两个概念:线程:进程中负责程序执行的执行单元。一个进程中至少有一个线程。多线程:解决多任务同时执行的需求,合理使用CPU资源。多线程的运行是根据CPU切换完成,如何切换由CPU决定,因此多线程运行具有不确定性。线程池定义线程池基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样

jiashuai94的博客 379

【Java开发300个实用技巧】178.异步线程池配置

本文深入探讨了Java异步线程池配置的五大核心技巧,帮助开发者构建高并发、高性能的系统。首先,详细解析了线程池参数的动态弹性策略,包括核心线程数、最大线程数和空闲回收策略的合理配置。其次,对比了常见队列类型的适用场景,如LinkedBlockingQueue、ArrayBlockingQueue和SynchronousQueue,并提供了选型建议。接着,强调了监控与调优的重要性,介绍了通过JMX和日志埋点实现全方位监控的方法。此外,提出了双重异常捕获机制,确保任务异常不被忽视。最后,总结了生产环境中常见的线

687

线程池——线程池

本文主要讲线程池:1)线程池的引入;2)Java标准库中的线程池;3)ThreadPoolExecutor 构造方法和拒绝策略;4)实现一个简单的线程池

2301_76937176的博客 1166

Java【多线程】(5)线程池

哈喽大家好吖,今天来给大家继续进行多线程——线程池方面的学习,线程池在以后项目中也是关键的存在,废话不多说让我们开始吧。

2301_81073317的博客 3410

Java 线程池

1 了解线程池 线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复的使用, 省去了频繁创建和销毁线程对象的操作,无需反复创建线程而消耗过多资源。 为什么要用线程池: 合理利用线程池能够带来三个好处 1.降低资源消耗。 – 减少了创建和销毁线程的次数,每个工作线程都 可以被重复利用,可执行多个任务。 2.提高响应速度 – 不需要频繁的创建线程,如果有 线程可以直接用,不会出现系统僵死! 3.提高线程的可管理性(线程池可以约束系统最多只能有多少个线程, 不会因为线程过多而死机) 线程池的核心思想:线程复

淋雨一直走~ 180

JAVA线程池学习小结和源码初探

目录1、创建一个线程池的基本方法1.1、参数解释1.2、方法说明2、几种常见线程池总结2.1、CachedThreadPool2.1.1、参数2.1.2、特点2.2、FixedThreadPool2.2.1、参数2.2.2、特点2.3、SingleThreadExecutor2.3.1、参数2.3.2、特点 1、创建一个线程池的基本方法 public ThreadPoolExecutor(int corePoolSize, int maximumP

dustinqi的博客 212

java线程池的面试题_献给准备面试的你,Java线程and线程池面试题小结

最近这几天一直在整理Java相关的面试题,“金九银十”是求职的最佳时间,但是现在的“银十”也已经过去了一半的时间,相信现在还在为面试四处奔波的小伙伴已经很疲惫了吧,下面就来减轻你负担,Java线程和线程池相关的面试题整理给大家,减轻你准备面试的负担。丑话说在前面,我“丑”我先说,嘿嘿。因为篇幅有限,所以这次的文章不会包含面试题的所有的内容,在这里求大家点一波关注啦!以后会持续更新哒!1、为什么用线...

weixin_42140716的博客 1212

线程池小结

系统启动一个线程的成本很高,所以用线程池可以很好的提高性能,特别是,需要创建大量生命期很短的线程,更应该用线程池。 线程池在启动的时候,就会创建大量空闲的线程,程序将Runnable,callable对象传给线程池,线程池就会启动一个线程执行run()或call()方法。执行完之后,线程也不会死,而是继续返回线程池,变成空闲的线程。 1.创建线程池 由Executors工厂类中的某个静态工厂...

张凡的博客 155
上一篇: 服务能力输出小结
下一篇: 关于Linux的TCP相关参数的整理
chengshuo678
博客等级 码龄14年 5粉丝 17原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值