Java 线程池

浅谈Java线程池,Java线程池解析。 掌握线程池是后端程序员的基本要求,相信大家求职面试过程中,几乎都会被问到有关于线程池的问题。我在网上搜集了几道经典的线程池面试题,并以此为切入点,谈谈我对线程池的理解。如果有哪里理解不正确,非常希望大家指出,接下来大家一起分析学习吧。 阅读详情

​创建一个线程池 ThreadPoolExecutor ,如何使用java线程池,线程池源码浅析

2014拍摄于四川羌族藏族自治区郎木寺下早课。

微信公众号

王皓的GitHub:https://github.com/TenaciousDWang

 

前面已经说了关于线程的东西,接下来说一下线程池。

 

前面我们知道多线程能够让我们充分的利用计算机资源,前面我们在使用线程时,是直接创建一个线程,创建时需要在内存划出线程的私有空间,比如程序计数器,本地方法栈等,当线程使用完毕时,系统需要回收这些空间,创建与回收都是需要消耗系统资源的,如果我们频繁的创建与销毁那么将会造成系统资源的大量浪费。

 

如果我们的线程能够复用,执行完任务后不用销毁,就可以继续执行下一个任务,这样就避免了重复创建销毁,提高了效率,节约了系统资源。这时候,我们就需要使用到线程池。

 

线程池的作用包括。

 

1、利用线程池管理和复用线程,控制最大并发。

 

2、实现任务线程队列缓存和拒绝机制。

 

3、实现计划和定时,周期执行。

 

4、线程池可以将不同功能的线程进行隔离,避免互相影响。

 

接下来我们看一下这个类,java.uitl.concurrent.ThreadPoolExecutor,在JUC包中ThreadPoolExecutor就是线程池最核心的类。

 

首先我们先看一下ThreadPoolExecutor的几个核心参数。

 

第一个corePoolSize。

 

 

常驻核心线程数,最小值为0,等于0时没有任何请求进入则销毁线程池,大于0时即使执行完所有任务也不会被销毁,注意如果这个值设置过小,则容易引起线程池频繁销毁和创建,过大则浪费资源,所以需要根据实际情况来设置。

 

第二个maximumPoolSize。

 

 

线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程,必须大于等于1,如果待执行的线程数量大于这个值,需要参考第五个参数,缓存于队列中,如果maximumPoolSize=corePoolSize则为固定大小线程池。

 

第三个keepAliveTime。

 

 

线程空闲时间,表示线程没有任务执行时最多保持多久时间会被销毁,直到剩下corePoolSize个数的线程为止,避免浪费。当线程数大于corePoolSize时才会起作用,也可以设置allowCoreThreadTimeOut变量设置为true时,核心线程超时也会被收回。

 

第四个TimeUnit,用来表示keepAliveTime的时间单位,通常为TimeUnit.SECONDS。

 

TimeUnit.DAYS;               //天

TimeUnit.HOURS;             //小时

TimeUnit.MINUTES;           //分钟

TimeUnit.SECONDS;           //秒

TimeUnit.MILLISECONDS;      //毫秒

TimeUnit.MICROSECONDS;      //微妙

TimeUnit.NANOSECONDS;       //纳秒

 

第五个workQueue。

 

 

缓存队列,当请求的线程数大于corePoolSize时,多出的线程进入BlockingQueue中。

 

第6个threadFactory。

 

 

线程工厂,生产一组相同任务的线程,线程池的命名可以通过给factory增加组名前缀来实现,在查看日志时更清晰。

 

 

可以参照自带的默认线程工厂实现写一个自己的。

 

第七个handler。

 

 

表示当拒绝处理任务时的策略,在当缓存队列达到上限时,且活动线程数大于maximumPoolSize的时候。是一种简单的限流保护。有以下四种取值。

 

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 

 

ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。

 

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)。

 

ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。 

 

举一个例子简单实现自定义的handler:

 

 

 

友好的拒绝策略,参考自《码出高效》:

 

1、保存到数据库进行削峰填谷,空闲时间提取执行。

 

2、转向某个提示页面。

 

3、打印日志。

 

假如有一个工厂,工厂里面有10(corePoolSize=10)个工人(线程),每个工人同时只能做一件任务(执行线程)。

 

只要当10个工人中有工人是空闲的,来了任务就分配给空闲的工人做。

 

当10个工人都有任务在做时,如果还来了任务,就把任务进行排队等待(阻塞队列缓存)。

 

如果说新任务数目增长的速度远远大于工人做任务的速度,那么此时工厂主管可能会想补救措施,比如重新招5个临时工人进来(maximumPoolSize=10+5)后就将任务也分配给这5个临时工人做。

 

如果说着15个工人做任务的速度还是不够,此时工厂主管可能就要考虑不再接收新的任务或者抛弃前面的一些任务了(handler处理策略)。

 

当这15个工人当中有人空闲时,而新任务增长的速度又比较缓慢,工厂主管可能就考虑辞掉5个临时工了,只保持原来的10个工人,毕竟请额外的工人是要花钱的(消耗资源的)。

 

线程池状态。

 

 

RUNNING:当创建线程池后,初始时,处于RUNNING状态的线程池能够接受新任务,以及对新添加的任务进行处理。

 

SHUTDOWN:如果调用了shutdown()方法,处于SHUTDOWN状态的线程池不可以接受新任务,但是可以对已添加的任务进行处理。

 

STOP:如果调用了shutdownNow()方法,处于STOP状态的线程池不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。

TIDYING:当所有的任务已终止,任务数量为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行函数terminated()。

 

 

terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。

 

TERMINATED:线程池彻底终止的状态。当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。

 

任务提交流程分析。

 

在ThreadPoolExecutor类中,最核心的任务提交方法是execute()方法,通过submit也可以提交任务,其中ExecutorService.submit()可以获取该任务执行的Future。实际上submit方法里面最终调用的还是execute()方法,所以我们这里以execute()方法为例来分析流程。

 

 

执行流程翻译如下:

 

1.如果线程池当前线程数小于corePoolSize,则调用addWorker创建新线程执行任务,成功返回true,失败执行步骤2。

 

2.如果线程池处于RUNNING状态,则尝试加入阻塞队列,如果加入阻塞队列成功,则尝试进行Double Check,如果加入失败,则执行步骤3。

 

3.如果线程池不是RUNNING状态或者加入阻塞队列失败,则尝试创建新线程直到maxPoolSize,如果失败,则调用reject()方法运行相应的拒绝策略。

 

在步骤2中如果加入阻塞队列成功了,则会进行一个Double Check的过程。Double Check过程的主要目的是判断加入到阻塞队里中的线程是否可以被执行。如果线程池不是RUNNING状态,则调用remove()方法从阻塞队列中删除该任务,然后调用reject()方法处理任务。否则需要确保还有线程执行。

 

private boolean addWorker(Runnable firstTask, boolean core){}

 

这个方法就不展开了,他会根据当前线程池状态,检查是否可以添加新的任务线程,如果可以则创建并启动任务,如果一切正常返回true,如果返回false的可能性如下:

 

1.线程池没有处于RUNNING状态,2.线程工厂创建新的任务线程失败。

 

接下来我们举一代码示例来看一下线程池的使用。首先创建一个任务。

 

 

然后我们创建一个test类,创建一个线程池,循环创建15个任务提交至线程池运行。

 

 

运行main方法。

 

 

当线程数超过5时,会将后面的线程放入队列中缓存,如果继续增加,则会创建新的线程直到maximumPoolSize为10时,也就是最多能存在maximumPoolSize加上缓存队列的线程,如果超出就会触发handler的拒绝策略。

 

我们在说ThreadPoolExecutor时,队列,线程工厂,拒绝服务策略都必须有实例对象,在实际编程中,却很少见过,在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类(java.util.concurrent.Executors)中提供的几个静态方法来创建线程池(带参ThreadFactory的原理一致不再赘述):

 

 

第一个newFixedThreadPool(int nThreads):输入固定的线程数,核心线程数,最大线程数一样,不存在空闲线程,keppAliveTime为0,需要注意这里new LinkedBlockingQueue<Runnable>()没有指定长度,最大值为Integer.MAX_VALUE非常危险会产生OOM,容易耗尽资源。

 

 

第二种newSingleThreadExecutor():创建一个单线程的线程池,相当于单线程串行执行所有任务,保证顺序,注意这里的new LinkedBlockingQueue<Runnable>()也没有定义长度。

 

 

第三种ScheduledThreadPoolExecutor(int corePoolSize)最大线程数直接拉到Integer.MAX_VALUE,存在OOM风险,我们看到他的队列,支持定时和周期性任务。

 

 

第四种newCachedThreadPool(),最大线程数拉到Integer.MAX_VALUE,也存在OOM,空闲线程60秒会回收。

 

 

第五种newWorkStealingPool(),JDK8引入,通过创建多个队列减少竞争,适合使用在很耗时的操作,但是newWorkStealingPool不是ThreadPoolExecutor的扩展,它是新的线程池类ForkJoinPool的扩展,但是都是在统一的一个Executor类中实现,由于能够合理的使用CPU进行对任务操作(并行操作),所以适合使用在很耗时的任务中。

 

最后引用《码出高效》的总结。

 

1、合理设置参数,根据实际工作场景配置线程数。

 

2、线程资源必须通过线程池提供,不允许私自显式创建线程。

 

3、创建线程与线程池指定有意义的线程名称,方便查询BUG。

 

4、线程池不允许使用Executors,而要通过ThreadPoolExcutor创建,明确线程池参数与运行规则,避免资源耗尽。

 

MATLAB实战:5步搞定铣削稳定性叶瓣图(附完整代码解析) 本文提供了一份基于MATLAB的铣削稳定性叶瓣图实战指南。通过5个清晰步骤,从动力学原理出发,详细解析了利用半离散法构建稳定性判据、模块化代码设计、高效参数扫描及高级可视化的完整流程,并附有完整代码,帮助工程师快速预测和规避加工颤振,优化铣削工艺参数。 阅读详情

相关推荐

手把手教你校准垂起固定翼(VTOL)遥控器与电调:从零到飞的完整流程

本文详细介绍了垂起固定翼(VTOL)遥控器与电调的校准流程,从基础认知到飞行前检查,涵盖遥控器通道配置、电调校准、舵机方向调整等关键步骤。通过图文并茂的指导,帮助无人机爱好者安全完成VTOL调试,确保首飞成功。特别强调多旋翼模式、过渡模式和固定翼模式的设置要点,适合新手和进阶用户参考。

jenkins8butler的博客 355

java面试线程池怎么说6_Java面试——线程池

1、类比介绍假如有一个工厂,工厂里面有10个工人,每个工人同时只能做一件任务。因此只要当10个工人中有工人是空闲的,来了任务就分配给空闲的工人做;当10个工人都有任务在做时,如果还来了任务,就把任务进行排队等待;如果说新任务数目增长的速度远远大于工人做任务的速度,那么此时工厂主管可能会想补救措施,比如重新招4个临时工人进来;然后就将任务也分配给这4个临时工人做;如果说着14个工人做任务的速度还是不...

weixin_39687667的博客 140

ZEMAX优化方法---【Zemax学习】

在光学系统自动设计中,优化设计是光学设计中的重要阶段,也是ZEMAX软件的核心功能,涉及评价函数、权因子、阻尼因子等重要概念。在常用的阻尼最小二乘法优化设计的基础上,ZEMAX给我们提供了三种优化选择:Optimization(局部优化)、Global Search(全局优化)和 Hammer Optimization(锤形优化)。

yh的博客 4447

spring 的线程池还是 java 的线程池

Java 提供了一个强大的并发库,其中包含了线程池的基本实现。线程池是接口的实现类,用于管理线程的创建、执行和终止。通过线程池,开发者可以避免频繁创建和销毁线程带来的开销,从而提升应用程序的性能。Spring 框架对 Java 的并发库进行了封装,使得线程池的配置更加简洁和灵活。Spring 提供了接口,这是对 JavaExecutor接口的扩展,允许开发者更容易地配置和管理线程池。Spring 的线程池配置通常通过 XML 或 Java 配置类来实现,并且 Spring 提供了多种实现,如等。

Flying_Fish_roe的博客 1673

嵌入式:简单的service框架

在嵌入式中,我们获取一个资源、设置一个变量、更新数据的话最直接的方式就是直接调用一个函数。这样的写法也可以满足基本的功能,前提是这个系统是只要一个任务,只有一个while(1)如图1.1。图1.1当有两个或者两个以上的任务的时候,同时调用一个函数(设置一个状态值、操作同一个资源、操作指针等行为)的流程如图1.2。图1.2当两个任务同时操作一个资源的时候,就会有重入的问题,会导致想不到的异常问题(数据不对、指针异常等)。

stupid的博客 986

DDR核心机制解析:从Burst到Prefetch的性能跃迁

本文深入解析了DDR内存性能跃迁的核心机制,重点阐述了Burst(突发传输)与Prefetch(预取)技术的协同工作原理。Burst机制通过连续传输数据减少访问延迟,而Prefetch则通过内部预取数据提升吞吐量,两者结合完美匹配现代CPU缓存行需求,是DDR3等内存实现高效带宽的关键。

weixin_29232121的博客 135

Java 线程池详解

Java线程池是一个管理线程的工具,它能够显著提高多线程程序的性能和可管理性。线程池是Java中用于管理和控制一组线程的机制,它可以有效地减少创建和销毁线程的开销,提高系统性能。线程池通过重复使用已存在的线程来执行新的任务,避免了频繁地创建和销毁线程所带来的资源消耗。优势特点降低资源消耗:避免过度创建线程导致的系统开销。提升响应速度:可以快速分配线程来执行任务,提高程序响应效率。增强可管理性:提供统一管理线程的手段,方便监控和调优。Executor。

weixin_42093668的博客 2070

STM32 USB键盘实现指南

在STM32上实现键盘功能可以通过USB HID(人机接口设备)协议来实现,使STM32设备能被计算机识别为标准键盘。通过上述步骤,您可以在STM32上实现一个功能完整的USB键盘。关键点包括:正确配置USB HID设备实现键盘矩阵扫描处理按键事件并生成HID报告优化按键响应和消抖处理添加额外功能如宏键和LED反馈这个实现可以作为基础,进一步扩展为游戏键盘、宏键盘或特殊输入设备。

suny8的专栏 968

java的线程池及动态线程池

Java 线程池(Thread Pool)是 Java 并发编程中的重要组件,主要用于管理和复用线程,以提高程序的并发性能、减少线程创建和销毁的开销,并防止资源耗尽。Java 提供了 包下的 框架来管理线程池。在高并发环境下,如果每个任务都创建一个新线程:Java 提供了 框架来管理线程池,其中主要的类包括: 是 Java 线程池的核心实现类,其构造方法如下: 3.1 参数解析: (核心线程数)(最大线程数) & (非核心线程存活时间 & 时间单位)(任务队列)(线程工厂)(拒绝策略)任

Hard_pea的博客 2261

Video Diffusion Models:基于扩散模型的视频生成

Diffusion Models专栏文章汇总:入门与实战 前言:扩散模型已经被广泛运用到图像生成、image-to-image转换、时间序列生成、3D点云生成、文本生成、音频生成等领域,谷歌研究院最新的研究成果成功将diffusion models运用到视频生成领域。 目录 模型结构 分解时空体系联合训练 梯度条件法 恐怖的实验结果 论文和代码 个人启发 个人疑问? 模型结构 在前面的博文中,我详细介绍了为什么绝大部分的diffusion models都使用Unet结构.

沉迷单车的追风少年 6669

iStoreOS等软路由插件卸载的残留?分享一款软路由高级卸载工具,自定义卸载,轻松解决卸载残留问题

iStoreOS用户看过来!👀 还在被插件包残留困扰吗?本文分享一款“高级卸载”的插件,助你深度清理iStoreOS系统,彻底解决软件卸载不净的痛点,告别频繁恢复出厂设置的烦恼!🚀

XiaoqiangClub的博客 1537

java线程池

ThreadPoolExecutor自定义参数。在Java并发编程中,线程池是管理线程资源的。自定义参数,避免资源耗尽风险。以下是几种常见线程池(通过。:在复杂生产系统中,

yuren_xia的博客 934

当AI学会写“自传”:OpenClaw 的 SOUL.md 如何把配置文件变成一颗会变形的心

SOUL.md。

步子哥的博客 1309

Java 线程池及线程池状态

Java 线程池及线程池状态

qq_41956014的博客 1万+

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Java线程池

Java线程池 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线...

weixin_41924879的博客 1188

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Java线程池原理及四种线程池的使用

Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执

honghailiang的专栏 1万+

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

java线程池之java自带的四种线程池

使用ThreadPoolExecutor创建线程池比较麻烦,对于很多人来说不易使用,所以java已经帮我们设计好了四种线程池。查看java源代码发现这四种线程池仍然是由ThreadPoolExecutor创建的,只不过把相关的参数已经设计好了而已。 案例一:同时演示三种线程池 /* * 任务类 */ class MyTask implements Runnable { String th

御前两把刀的专栏 1177

【从0到1学习Java线程池】Java线程池的简介以及使用

这是【从0到1学习Java线程池】系列文章的第 壹 篇,该系列文章总共三篇,介绍了 Java 线程池的使用以及原理,并且最后会实现一个基本的线程池。本篇文章主要介绍了 Java 线程池以及它的使用。

远航 | FIBOS.io 3785

java线程池【通过源码角度看java线程池】

在查看源码之前,还是先看下整体的调用流程图,有个直观的感受。线程池提交任务的流程:调用线程池的 execute(Runnable command) 方法提交一个 Runnable 对象。首先,线程池会检查当前活跃线程的数量是否小于 corePoolSize。如果线程数小于 corePoolSize,则会创建一个新的线程来执行提交的 Runnable 任务。如果当前活跃线程数大于或等于 corePoolSize,则会尝试将 Runnable 加入到工作队列(workQueue)中。

m0_50149847的博客 1434

java线程池:线程池怎么开启的、线程池怎么工作的、线程池是怎么关闭的

1.序 位于 java.util.concurrent 下的 ThreadPoolExecutor 是java的线程池,可以用来管理线程。 本文围绕线程池的开启、工作、关闭三个方向进行探究,去追溯源码的实现。 2.线程池怎么开启的 ThreadPoolExecutor 有四个构造方法,如下: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit, B.

Yuwen_forJava的博客 3789
上一篇: Java BlockingQueue 阻塞式队列
下一篇: Java 并发编程 Condition
JavaBoy王皓
博客等级 码龄11年 23粉丝 46原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值