聊聊并发编程——线程池

目录

Java线程池

处理流程

线程池主要参数

常见的拒绝策略

execute和submit区别

关闭线程池

常见的线程池

newSingleThreadExecutor

newFixedThreadPool

newCachedThreadPool

newScheduledThreadPool

线程池的状态


Java线程池

运用场景最多的并发框架,几乎所有需要异步或者并发执行任务的程序都可以使用线程池。如下好处:

  • 降低资源消耗。重复利用已创建的线程降低线程创建和销毁造成的消耗。

  • 提高响应速度。当任务到达时,不需要等到线程创建就能立即执行。

  • 提高线程的客观理性。使用线程池可以进行统一分配、调优和监控。

处理流程

当新任务到达线程池的时候,处理流程如下:

  1. 判断核心线程池里的线程是否都在执行任务:

    • 否,创建新的工作线程执行任务。

    • 是,去看阻塞队列。

  2. 判断阻塞队列是否满了:

    • 否,将新任务存储在阻塞队列中。

    • 是,去看整个线程池。

  3. 判断线程池的线程是否都处于工作状态:

    • 否,创建新的工作线程执行任务。

    • 是,去看拒绝策略吧。

  4. 根据拒绝策略进行处理。

ThreadPoolExecutor执行execute方法分为下面4中情况:

  1. 当前运行的线程少于corePoolSize,创建新线程执行任务。(需获取全局锁)

  2. 运行的线程等于或多余corePoolSize,则将任务加入BlockingQueue。

  3. 如果队列已满,且运行线程数少于maximumPoolSize,创建新线程执行任务。(需获取全局锁)

  4. 如果创建新线程将使得当前运行的线程超过maximumPoolSize,任务被拒绝,执行RejectedExecutionHandler.rejectedExecution()方法。

网上有看到一个生动的例子,可能会让大家印象更深刻些:

银行的营业大厅有6个业务窗口,现在开放了3个,有3个业务员负责办理业务。这天,二柱去办理业务,他可能会遇到什么情况呢?

  1. 3个业务窗口正好空闲,他可以直接去办理业务。

  2. 3个业务窗口都有人,所以他得去排队。

  3. 3个业务窗口都有人而且队也排满了。这是经理赶紧开放了另外3个窗口,排队的可以先去办理。

  4. 6个业务窗口都有人了,队伍也排满了。二柱子去找经理,经理说明天再来或者先不办了。

线程池主要参数
  1. 核心线程数(corePoolSize): 核心线程数是线程池中保持活动状态的最小线程数量。即使线程池中没有任务需要执行,核心线程也会保持活动状态,不会被销毁。核心线程数通常用来处理短期生存期的任务,以减少线程的创建和销毁开销。

  2. 最大线程数(maximumPoolSize): 最大线程数是线程池中允许存在的最大线程数量。当任务数量超过核心线程数并且任务队列已满时,线程池会创建新的线程,但不会超过最大线程数。最大线程数可以控制线程池的最大并发性。

  3. 任务队列(BlockingQueue): 任务队列用于存储等待执行的任务。当线程池中的线程数达到核心线程数时,新的任务会被放入任务队列中等待执行。任务队列可以是不同类型的队列,如无界队列(如 LinkedBlockingQueue)或有界队列(如 ArrayBlockingQueue)。

  4. 线程存活时间(keepAliveTime): 线程存活时间是在核心线程数之外的线程在没有任务可执行时保持活动状态的最长时间。当线程池中的线程数量超过核心线程数,空闲的非核心线程在经过一定时间后会被销毁,以减少资源占用。

  5. 拒绝策略(RejectedExecutionHandler): 拒绝策略定义了当线程池无法接受新的任务时应该采取的动作。常见的拒绝策略包括抛出异常、丢弃任务、丢弃最旧的任务、调用提交任务的线程来执行任务等。

  6. 线程工厂(ThreadFactory): 线程工厂用于创建线程池中的线程。通过自定义线程工厂,可以为线程池中的线程设置自定义的名称、优先级、守护状态等属性。

常见的拒绝策略
  1. AbortPolicy(默认策略): 这是默认的拒绝策略,它会抛出一个 RejectedExecutionException 异常,告诉调用者线程池已满,无法处理新任务。这是最常用的拒绝策略,它会防止任务堆积,但可能会导致任务丢失。

    ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue,new ThreadPoolExecutor.AbortPolicy());
  2. CallerRunsPolicy: 这个策略不会抛出异常,而是将任务回退到调用者,让调用者来执行。这可以用于保证任务的执行,但可能会导致调用者的线程阻塞。

    ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue,new ThreadPoolExecutor.CallerRunsPolicy());
  3. DiscardPolicy: 这个策略会默默地丢弃掉无法处理的新任务,不会抛出异常。这可能会导致任务丢失,但对于不太重要的任务可以使用。

    ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue,new ThreadPoolExecutor.DiscardPolicy());
  4. DiscardOldestPolicy: 这个策略会丢弃队列中最旧的任务,然后尝试重新提交新任务。它可能会导致一些任务被丢弃,但对于有优先级的任务可能是一个不错的选择。

    ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue,new ThreadPoolExecutor.DiscardOldestPolicy());
  5. 自定义策略: 你也可以自定义拒绝策略,只需实现 RejectedExecutionHandler 接口的 rejectedExecution 方法,然后将其传递给线程池的构造函数。

    class CustomRejectPolicy implements RejectedExecutionHandler {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {// 自定义拒绝策略的逻辑}
    }
    ​
    ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue,new CustomRejectPolicy());

选择适当的拒绝策略取决于应用程序的需求。通常,如果任务的重要性很高,你可能会选择将任务回退到调用者或者使用自定义策略来处理。如果任务不太重要,你可以使用默认的 AbortPolicyDiscardPolicy 策略。需要根据具体情况权衡任务的执行和资源利用之间的权衡。

execute和submit区别

可以使用两个方法向线程池提交任务,分别为execute()和submit()方法。

execute()方法用于提交不需要返回值的任务。

 threadsPool.execute(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } });

submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个 future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方 法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线 程一段时间后立即返回,这时候有可能任务没有执行完。

Future future = executor.submit(harReturnValuetask); try { Object s = future.get(); } catch (InterruptedException e) { // 处理中断异常 } catch (ExecutionException e) { // 处理无法执行任务异常 } finally { // 关闭线程池 executor.shutdown(); }
关闭线程池

可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。

shutdown() 将线程池状态置为shutdown,并不会⽴即停⽌:

  1. 停⽌接收外部submit的任务

  2. 内部正在跑的任务和队列⾥等待的任务,会执⾏完

  3. 等到第⼆步完成后,才真正停⽌

shutdownNow() 将线程池状态置为stop。⼀般会⽴即停⽌,事实上不⼀定:

  1. 和shutdown()⼀样,先停⽌接收外部提交的任务

  2. 忽略队列⾥等待的任务

  3. 尝试将正在跑的任务interrupt中断

  4. 返回未执⾏的任务列表

shutdown 和shutdownnow简单来说区别如下:

  • shutdownNow()能⽴即停⽌线程池,正在跑的和正在等待的任务都停下了。这样做⽴即⽣效,但是⻛险也⽐ 较⼤。

  • shutdown()只是关闭了提交通道,⽤submit()是⽆效的;⽽内部的任务该怎么跑还是怎么跑,跑完再彻底停 ⽌线程池。

常见的线程池

常见的有四种,都是通过Executors 工具类创建的,但是不建议。Executors各个方法的弊端:newFixedThreadPool和newSingleThreadExecutor: 主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。

newCachedThreadPool和newScheduledThreadPool: 主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

newSingleThreadExecutor

直接调用ThreadPoolExecutor的构造⽅法。

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory));
}

线程池特点:

  • 核心线程数1

  • 最大线程数1

  • 阻塞队列是无界队列LinkedBlockingQueue,可能会导致OOM

  • keepAliveTime为0

使用场景:串行执行任务的场景,一个任务一个任务地执行

newFixedThreadPool

直接调用ThreadPoolExecutor的构造⽅法。

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory);
}

线程池特点:

  • 核心线程数和最大线程数大小一样

  • keepAliveTime为0

  • 阻塞队列是无界队列LinkedBlockingQueue,可能会导致OOM

使用场景:适用于执行长期的任务

newCachedThreadPool

直接调用ThreadPoolExecutor的构造⽅法。

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory);
}

线程池特点:

  • 核心线程数0

  • 最大线程数Integer.MAX_VALUE,可能无限创建线程,导致OOM

  • 阻塞队列是SynchronousQueue

  • 非核心线程空闲存活时间为60s

使用场景:并发执行大量短期的小任务

newScheduledThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}

线程池特点:

  • 最大线程数Integer.MAX_VALUE,可能无限创建线程,导致OOM

  • 阻塞队列DelayedWorkQueue

  • keepAliveTime为0

  • scheduleAtFixedRate() :按某种速率周期执⾏

  • scheduleWithFixedDelay():在某个延迟后执⾏

使用场景:周期性执行任务的场景,需要限制线程数量的场景

线程池的状态
  1. RUNNING(运行中): 线程池处于运行状态,可以接受新的任务,并且正在执行任务队列中的任务。

  2. SHUTDOWN(关闭中): 线程池进入关闭状态,不再接受新的任务提交,但会继续执行已经提交的任务,直到所有任务都完成。

  3. STOP(停止中): 线程池进入停止状态,不再接受新的任务提交,并且尝试中断正在执行的任务。这个状态会尽量终止线程池的执行。

  4. TIDYING(整理中): 线程池正在执行清理操作。当线程池的任务都完成后,它会进入这个状态,执行一些必要的清理操作,例如关闭线程。

  5. TERMINATED(已终止): 线程池已经完全终止,不再有任何活动线程。线程池的状态将永远停留在这个状态。

线程池的状态通常通过调用线程池的方法来进行转换,例如 shutdown() 方法将线程池从 RUNNING 状态转换为 SHUTDOWN 状态,shutdownNow() 方法将线程池从 RUNNING 状态转换为 STOP 状态。一旦线程池进入 TERMINATED 状态,就无法再切换到其他状态。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/93353.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里巴巴K8S集成seata

正文 在K8S集成seata&#xff0c;官方配置 代码 apiVersion: v1 kind: Service metadata:name: seata-servernamespace: wmz-devlabels:k8s-app: seata-server spec:type: NodePortports:- port: 8091nodePort: 30091protocol: TCPname: httpselector:k8s-app: seata-server-…

Java练习题-键盘录入字符串实现大小写转换

✅作者简介&#xff1a;CSDN内容合伙人、阿里云专家博主、51CTO专家博主、新星计划第三季python赛道Top1&#x1f3c6; &#x1f4c3;个人主页&#xff1a;hacker707的csdn博客 &#x1f525;系列专栏&#xff1a;Java练习题 &#x1f4ac;个人格言&#xff1a;不断的翻越一座又…

idea清空缓存类

解决办法 网上有很多是让你去清空什么maven依赖&#xff0c;但假如这个项目是你不可以大刀阔斧的话 可以清空idea缓存 选择 Invalidate 开头的 然后全选 运行重启idea OK

Linux系统编程系列之线程

一、什么是线程 线程&#xff08;Thread&#xff09;是计算机中的基本执行单元&#xff0c;是操作系统调度的最小单位。线程是进程内的一个独立执行流程&#xff0c;一个进程可以包含多个线程&#xff0c;这些线程共享进程的资源&#xff0c;但每个线程都有自己的独立栈空间以及…

Java后端模拟面试,题集①

1.Spring bean的生命周期 实例化 Instantiation属性赋值 Populate初始化 Initialization销毁 Destruction 2.Spring AOP的创建在bean的哪个时期进行的 &#xff08;图片转载自Spring Bean的完整生命周期&#xff08;带流程图&#xff0c;好记&#xff09;&#xff09; 3.MQ如…

基于SSM的选课排课系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

2023年中国奶牛平均单产量、奶类产量及发展趋势分析:液态奶市场向高端化发展[图]

2022年&#xff0c;我国奶产业素质稳步提升&#xff0c;全国存栏百头以上规模养殖比例达到72%&#xff0c;同比提高2个百分点。奶牛平均单产9.2吨&#xff0c;较2021年增加500千克&#xff1b;规模牧场95%以上配备全混合日粮搅拌车&#xff0c;原料奶生产100%实现机械化挤奶&am…

3D WEB轻量化引擎HOOPS助力3D测量应用蓬勃发展:效率、精度显著提升

在3D开发工具领域&#xff0c;Tech Soft 3D打造的HOOPS SDK已经崭露头角&#xff0c;成为了全球领先的3D领域开发工具提供商。HOOPS SDK包括四种不同的3D软件开发工具&#xff0c;已成为行业的翘楚。 其中&#xff0c;HOOPS Exchange以其CAD数据转换的能力脱颖而出&#xff0c…

如何破解压缩包zip解压密码?

Zip压缩包设置了密码&#xff0c;解压的时候就需要输入正确对密码才能顺利解压出文件&#xff0c;正常当我们解压文件或者删除密码的时候&#xff0c;虽然方法多&#xff0c;但是都需要输入正确的密码才能完成。忘记密码就无法进行操作。 那么&#xff0c;忘记了zip压缩包的密…

leetCode 55.跳跃游戏 贪心算法

给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入…

基于微信小程序的手机在线商城小程序设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

MySql017——组合查询

一、UNION作用 可用UNION操作符来组合数条SQL查询。 二、UNION 使用规则 1、UNION的使用很简单。所需做的只是给出每条SELECT语句&#xff0c;在各条语句之间放上关键字UNION。2、UNION必须由两条或两条以上的SELECT语句组成&#xff0c;语句之间用关键字UNION分隔&#xff…

专业图像处理软件DxO PhotoLab 7 mac中文特点和功能

DxO PhotoLab 7 mac是一款专业的图像处理软件&#xff0c;它为摄影师和摄影爱好者提供了强大而全面的照片处理和编辑功能。 DxO PhotoLab 7 mac软件特点和功能 强大的RAW和JPEG格式处理能力&#xff1a;DxO PhotoLab 7可以处理来自各种相机的RAW格式图像&#xff0c;包括佳能、…

(SAR)Sentinel-1影像自动下载

基于ASF网站提供的python代码&#xff0c;实现Sentinel-1影像的自动下载&#xff1b; 1、登录ASF网站 登录Sentinel-1影像ASF网站&#xff1a;https://search.asf.alaska.edu/&#xff1b; 点击网站最右侧Sign in图标&#xff0c;进行用户注册&#xff1b; 注册完用户之后&…

【c语言】推箱子

所需知识&#xff1a;c语言枚举&#xff0c;数组&#xff0c;for循环&#xff0c;while循环&#xff0c;switch,case语句&#xff0c;图形库相关函数 1.调整控制台窗口大小 #define _CRT_SECURE_NO_WARNINGS #include <stdlib.h>#include <stdio.h> int main() {…

STM32之DMA

简介 • DMA &#xff08; Direct Memory Access &#xff09;直接存储器存取 &#xff08;可以直接访问STM32内部存储器&#xff0c;如SRAM、程序存储器Flash和寄存器等&#xff09; •DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输&#xff0c;无须CPU干预&a…

pytorch函数reshape()和view()的区别及张量连续性

目录 1.view() 2.reshape() 3.引用和副本&#xff1a; 4.区别 5.总结 在PyTorch中&#xff0c;tensor可以使用两种方法来改变其形状&#xff1a;view()和reshape()。这两种方法的作用是相当类似的&#xff0c;但是它们在实现上有一些细微的区别。 1.view() view()方法是…

古记事法:Windows 下 16 位汇编环境搭建指南(DOSBox-X 篇)

文章目录 参考环境DOSBox-XWOWWindows On Windows 产生的原因Windows On Windows 的工作原理WOW16 的结束与 WOW64 的未来 在现代操作系统中运行 16 位应用程序DOSBox-X 16 位汇编环境的搭建应用准备挂载自动挂载dosbox-x.conf配置工具 参考 项目描述搜索引擎Bing、GoogleAI 大…

二极管的直流等效电路和微变等效电路

二级管的主要参数 1.IF&#xff08;最大整流的电流&#xff09; 二极管长期工作做能够通过电流的平均最大值&#xff1a;物理意义&#xff1a;功率电流值。 2.UR 二极管最高反向工作电压 需要留有裕度&#xff0c;通常能达到一半的裕度&#xff1b;UR不能等于UBR。 3.IR 未击穿…

自动驾驶技术:现状与未来

自动驾驶技术&#xff1a;现状与未来 文章目录 引言自动驾驶技术的现状自动驾驶技术的挑战自动驾驶技术的未来结论结论 2023星火培训【专项营】Apollo开发者社区布道师倾力打造&#xff0c;包含PnC、新感知等的全新专项课程上线了。理论与实践相结合&#xff0c;全新的PnC培训不…