Java并发编程(六)线程池[Executor体系]

概述

在处理大量任务时,重复利用线程可以提高程序执行效率,因此线程池应运而生。

  • 它是一种重用线程的机制,可以有效降低内存资源消耗
  • 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行
  • 线程池可以帮助我们更好地管理线程的生命周期和资源使用,避免线程频繁地创建和销毁带来的性能问题

同时,线程池还可以提供一些额外的功能,例如线程池的大小控制、线程池的任务队列、线程池的拒绝策略等。线程池中通常维护一个线程队列,线程队列中保存着已创建的线程,当有新的任务需要执行时,线程池中的线程就可以从队列中取出一个线程来执行任务,任务执行完毕后线程可以被放回线程队列中,等待下一个任务的到来

核心执行流程

线程池执行流程
  • 当有新任务需要线程执行时,线程池会先判断是否有空闲的核心线程,如果有则将任务分配给其中一个空闲的核心线程执行。如果没有空闲的核心线程或者核心线程的数量还没达到最大值,则创建一个新的核心线程来执行任务
  • 若核心线程已经达到最大值且工作队列未满,则将新提交的任务存储在这个工作队列中。如果工作队列已满,则会判断线程池中的线程数是否已经达到最大值,如果没有则创建一个新的非核心线程来执行任务
  • 若工作队列已满且线程池中的线程都已经处于工作状态,即核心线程和非核心线程都在执行任务,则交给饱和策略来处理这个任务。饱和策略可以决定如何处理无法处理的任务,例如抛出异常或者阻塞任务提交

线程池状态

  • RUNNING:该状态代表能接受新任务以及处理任务(初始状态)
  • SHUTDOWN:该状态代表不接受新任务,但处理已添加的任务(调用shutdown()时,由RUNNING->SHUTDOWN)
  • STOP:该状态时表示不接受新任务,不处理已添加任务,并会中断正在处理中的任务(调用shutdownNow()时,由RUNNING或者SHUTDOWN→STOP)
  • TIDYING:进入SHUTDOWN或者STOP状态后,所有任务都被处理或者清理干净后就会进入该状态,同时会执行terminated()方法(该方法是个钩子函数,自定义实现)
  • TERMINATED:结束状态,执行完terminated方法后由TIDYING->TERMINATED

Executor 框架

两级调度模型 

在HotSpotVM的线程模型中,Java线程(java.lang.Thread)被一对一映射为本地操作系统线程
Java线程启动时会创建一个本地操作系统线程;当该 Java 线程终止时,这个操作系统线程也会被回收。操作系统会调度所有线程并将它们分配给可用的CPU

  • 在上层架构中,Java多线程程序通常把应用分解为若干个任务,应用程序通过Executor框架将这些任务映射为固定数量的线程
  • 在下层架构中,操作系统内核将这些线程映射到硬件处理器上,下层的调度不受应用程序的控制

Executor结构

Executor框架包含的主要的类与接口如图所示

  • Executor是一个接口,它是Executor框架的基础,它将任务的提交与任务的执行分离开来
  • ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务
  • ScheduledThreadPoolExecutor是一个实现类,用来延迟之后执行任务或者定时执行任务。ScheduledThreadPoolExecutor比Timer更灵活,功能更强大
  • 实现Future接口的FutureTask类,代表异步计算的结果
  • Runnable接口和Callable接口的实现类,都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行

ThreadPoolExecutor

构造函数详细说明

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.acc = System.getSecurityManager() == null ?null :AccessController.getContext();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;
}

参数介绍:

  • corePoolSize:核心线程池个大小。一般来说任务比较耗时可以配CPU核数*2,因为这样可以充分利用CPU,任务小并且执行很快则可以配CPU核数+1或者更小(因为线程上下文切换耗时)(获取CPU核心数:Runtime.getRuntime().availableProcessors())
  • maximumPoolSize:最大线程池大小。当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务,总线程数≤maximumPoolSize
  • keepAliveTime:空闲时间,超过核心线程数的线程在到达空闲时间后会被销毁
  • TimeUnit : 时间单位
  • BlockingQueue:用来暂时保存任务的队列(阻塞队列)
  • ThreadFactory:自定义的线程工厂,默认是一个新的、非守护线程并且不包含特殊的配置信息,我们也可以自定义加入我们的调试信息,比如线程名称、错误日志等
  • RejectedExecutionHandler:饱和策略。当线程数=maximumPoolSize,且任务队列已满时,多余的任务需要采取的措施,有以下几种(默认AbortPolicy):
    • AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
    • DiscardPolicy: 丢掉这个任务并且不会有任何异常
    • DiscardOldestPolicy:丢弃最老的。也就是说如果队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列
    • CallerRunsPolicy:主线程会自己去执行该任务,不会等待线程池中的线程去执行
    • 自定义:当然也可以自定义策略

常用ThreadPoolExecutor类型

ThreadPoolExecutor通常使用工厂类Executors来创建,包括3种ThreadPoolExecutor类型:

  • FixedThreadPool:可重用固定线程数的线程池
  • SingleThreadExecutor:单个线程的线程池(只有一个工作线程)
  • CachedThreadPool:根据需要创建新线程的线程池
FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
  • 其中corePool和maximumPoolSize 都被设置成指定的参数
  • keepAliveTime设置为0L,意味着多余的空闲线程会被立即终止
  • 适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场景,更适合于负载比较重的服务器
fixedThreadPool执行流程
  • 如果当前线程少于corePool,则创建新线程来执行当前任务
  • 当运行的线程数等于corePool之后,将任务加入LinkedBlockingQueue队列中
  • 线程执行完当前任务后会循环反复从LinkedBlockingQueue获取任务来执行

由于为LinkedBlockingQueue无界队列(长度Integer.MAX_VALUE),所以会出现如下情景:

  • maximumPoolSize和keepAliveTime参数将会无效,因为maximumPoolSize=corePool
  • 不会拒绝任务,因为是无界队列,任务不会满
SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}

corePool和maximumPoolSize 均被设置成了1,其他影响和运行方式都与FixedThreadPool相同

CachedThreadPool
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}
  • corePoolSize核心线程数为0,而maximumPoolSize为Integer.MAX_VALUE(21亿多),意味着没有空闲线程就会不断的创建线程去执行,极端情况会耗尽CPU和内存资源;
  • keepAliveTime=60s后空闲线程会被终止,所以长时间内保持空闲的情况下不会占用任何资源
  • SynchronousQueue是没有容量的阻塞队列,每个插入操作都会等待另一个线程对应的取出操作
  • 适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器

ScheduleThreadPoolExecutor

构造函数

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

ScheduledThreadPoolExecutor为了实现周期性任务对ThreadPoolExecutor做了如下修改:

  • 使用DelayedWorkQueue作为任务队列
  • 获取任务的方式不同,同样都是队列的take,但增加了时间的判断
  • 执行周期任务后,增加了额外的处理(需要把任务重新添加进队列)

常用ScheduleThreadPoolExecutor类型

ScheduledThreadPoolExecutor通常使用工厂类Executors来创建,2种类型:

  • ScheduledThreadPoolExecutor: 包含若干个线程 ScheduledThreadPoolExecutor
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) 
    • ScheduledThreadPoolExecutor 适用于需要多个后台线程执行周期任务,同时为了满足资源管理的需求而需要限制后台线程的数量的应用场景

  • SingleThreadScheduledExecutor: 只包含一个线程 ScheduledThreadPoolExecutor
    public static ScheduledExecutorService newSingleThreadScheduledExecutor()
    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
    • SingleThreadScheduledExecutor 适用于需要单个后台线程执行周期任务,同时需要保证顺序地执行各个任务的应用场景

FutureTask

Future接口和实现Future接口的FutureTask类用来表示异步计算的结果。当我们把Runnable接口或Callable接口的实现类提交(submit)给 ThreadPoolExecutor或ScheduledThreadPoolExecutor时,ThreadPoolExecutor或ScheduledThreadPoolExecutor会向我们返回一个FutureTask对象

Executor 实践

ThreadPoolExecutor

package com.bierce;
import java.util.concurrent.*;
public class TestThreadPoolExecutor {public static void main(String[] args) throws ExecutionException, InterruptedException {//创建线程池ExecutorService executorService = Executors.newFixedThreadPool(5);//方式一:Runnable方式的分配10个任务提交给线程池ThreadPoolDemo threadPoolDemo = new ThreadPoolDemo();for (int i = 0; i <= 10; i++) {executorService.submit(threadPoolDemo);}//方式二:Callable方式的分配10个任务提交给线程池for (int i = 0; i <= 10; i++) {Future<Object> sum = executorService.submit(() -> {int sum1 = 0;for (int i1 = 1; i1 <= 100; i1++) {sum1 += i1;}return sum1;});System.out.println(Thread.currentThread().getName() + ":" + sum.get()); //main:5050}//关闭线程池executorService.shutdown();}
}
class ThreadPoolDemo implements Runnable{private int i = 0;@Overridepublic void run() {while (i<10){System.out.println(Thread.currentThread().getName() + ":" + i++);}}
}

ScheduleThreadPoolExecutor

package com.bierce;
import java.util.Random;
import java.util.concurrent.*;
public class TestScheduleThreadPool {public static void main(String[] args) throws ExecutionException, InterruptedException {ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);for (int i = 1; i <=5 ; i++) {//调用schedule方法执行任务Future<Integer> random = scheduledExecutorService.schedule(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int random = new Random().nextInt(100);System.out.println(Thread.currentThread().getName() + ":" + random);return random;}},1,TimeUnit.SECONDS); //每隔一秒执行一个任务System.out.println(random.get());}scheduledExecutorService.shutdown(); //关闭线程池}
}

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

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

相关文章

【TI毫米波雷达笔记】MMWave配置流程避坑

【TI毫米波雷达笔记】MMWave配置流程避坑 在TI SDK目录下的mmwave.h文档说明中 强调了要按以下配置&#xff1a; mmWave API The mmWave API allow application developers to be abstracted from the lower layer drivers and the mmWave link API.The mmWave file should b…

74、75、76——tomcat项目实战

tomcat项目实战 tomcat 依赖 java运行环境,必须要有jre , 选择 jdk1.8 JvmPertest 千万不能用 kyj易捷支付 项目机器 选择 一台机器 ,安装jdk1.8的机器下载tomcat的包 上传到机器,解压tomcattomcat文件 bin文件夹: 启动文件 堆栈配置文件 catalina.sh JAVA_OPTS="-Xm…

【分布式存储】数据存储和检索~LSM

在数据库领域&#xff0c;B树拥有无可撼动的地位&#xff0c;但是B树的缺点就是在写多读少的场景下&#xff0c;需要进行大量随机的磁盘IO读写&#xff0c;而这个性能是最差的。并且在删除和添加数据的时候&#xff0c;会造成整个树进行递归的合并、分裂&#xff0c;数据在磁盘…

java+springboot+mysql小区宠物管理系统

项目介绍&#xff1a; 使用javaspringbootmysql开发的小区宠物管理系统&#xff0c;系统包含超级管理员&#xff0c;系统管理员、用户角色&#xff0c;功能如下&#xff1a; 超级管理员&#xff1a;管理员管理&#xff1b;用户管理&#xff1b;宠物分类&#xff1b;宠物管理&…

提高考试成绩的有效考试培训系统

近年来&#xff0c;随着考试竞争的日益激烈&#xff0c;对于学生来说&#xff0c;提高考试成绩已成为一项重要的任务。为了帮助学生有效提升考试成绩&#xff0c;我们开发了一套全面而详细的有效的考试培训系统。 该培训系统作为一种全新的教学方法&#xff0c;力求通过提供多…

uni-app使用vue语法进行开发注意事项

目录 uni-app 项目目录结构 生命周期 路由 路由跳转 页面栈 条件编译 文本渲染 样式渲染 条件渲染 遍历渲染 事件处理 事件修饰符 uni-app 项目目录结构 组件/标签 使用&#xff08;类似&#xff09;小程序 语法/结构 使用vue 具体项目目录如下&#xff1a; 生命…

[QT编程系列-41]:Qt QML与Qt widget 深入比较,快速了解它们的区别和应用场合

目录 1. Qt QML与Qt widget之争 1.1 出现顺序 1.2 性能比较 1.3 应用应用领域 1.4 发展趋势 1.5 QT Creator兼容上述两种设计风格 2. 界面描述方式的差别 3. QML和Widgets之间的一些比较 4. 选择QML和Widgets之间的Qt技术时&#xff0c;可以考虑以下几个因素&#xff…

初始多线程

目录 认识线程 线程是什么&#xff1a; 线程与进程的区别 Java中的线程和操作系统线程的关系 创建线程 继承Thread类 实现Runnable接口 其他变形 Thread类及其常见方法 Thread的常见构造方法 Thread类的几个常见属性 Thread类常用的方法 启动一个线程-start() 中断…

JVM内存管理

文章目录 1、运行时数据区域1.1 程序计数器&#xff08;线程私有&#xff09;1.2 JAVA虚拟机栈&#xff08;线程私有&#xff09;1.3 本地方法栈1.4 Java堆&#xff08;线程共享&#xff09;1.5 方法区&#xff08;线程共享&#xff09;1.6 直接内存&#xff08;非运行时数据区…

2023牛客暑期多校训练营9-Non-Puzzle: Segment Pair

2023牛客暑期多校训练营9-Non-Puzzle: Segment Pair https://ac.nowcoder.com/acm/contest/57363/I 文章目录 2023牛客暑期多校训练营9-Non-Puzzle: Segment Pair题目大意解题思路代码 题目大意 解题思路 对于每一对 [ l i , r i ] [l_i,r_i] [li​,ri​]和 [ l i ′ , r i …

Linux命令200例:adduser用于创建新用户

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &…

[LeetCode - Python] 11.乘最多水的容器(Medium);26. 删除有序数组中的重复项(Easy)

1.题目&#xff1a; 11.乘最多水的容器&#xff08;Medium&#xff09; 1.代码 1.普通双指针对撞 贪心算法 class Solution:def maxArea(self, height: List[int]) -> int:# 对撞双指针# 对比记录最大面积&#xff0c;并移动短板&#xff0c;重新计算&#xff1b;left,…

SpringBoot整合WebSocket详解

环境&#xff1a;Springboot3.0.5 WebSocket介绍 WebSocket协议RFC 6455提供了一种标准化的方式&#xff0c;通过一个TCP连接在客户端和服务器之间建立全双工、双向的通信通道。它是一个不同于HTTP的TCP协议&#xff0c;但设计为在HTTP之上工作&#xff0c;使用80和443端口&am…

Excel革命,基于电子表格开发的新工具,不是Access和Power Fx

深谙其道 在日常工作中&#xff0c;Excel是许多人不可或缺的办公工具。 是微软的旗下产品&#xff0c;属于Microsoft 365套件中的一部分&#xff0c;强大的数据处理和计算功能&#xff0c;被普遍应用在全球各行各业的人群当中&#xff0c;是一款强大且普及的电子表格软件。 于…

Do not access Object.prototype method ‘hasOwnProperty‘ from target object

调用 hasOwnProperty 报错&#xff1a;不要使用对象原型上的方法&#xff0c;因为原型的方法可能会被重写 if (this.formData.selectFields.hasOwnProperty(selectField)) {delete this.formData.selectFields[selectField];} else {this.formData.selectFields[selectField] …

【FastColoredTextBox】C# 开源文本编辑控件

主界面截图 使用Demos演示 FastColoredTextBox 是一个用于在 C# 程序中实现高亮语法着色、代码编辑和文本显示的自定义控件。它提供了许多功能&#xff0c;包括&#xff1a; 语法高亮&#xff1a;FastColoredTextBox 支持多种语言的语法高亮&#xff0c;可以根据语法规则将不同…

安路FPGA的赋值报错——移位处理,加括号

authordaisy.skye的博客_CSDN博客-嵌入式,Qt,Linux领域博主 在使用移位符号用来当作除以号使用时&#xff0c;发现如下问题 其中 cnt_8K 为偶数和奇数时输出的数据不一样 reg [10:0] cnt_8K; reg [10:0] ram1_addra; always(posedge clk_16M) begin if(ram_out_flag )begin if(…

【Servlet】(Servlet API HttpServlet 处理请求 HttpServletRequest 打印请求信息 前端给后端传参)

文章目录 Servlet APIHttpServlet处理请求 HttpServletRequest打印请求信息前端给后端传参 Servlet API Servlet中常用的API HttpServlet 实际开发的时候主要重写 doXXX 方法, 很少会重写 init / destory / service destory 服务器终止的时候会调用. //下面的注解把当前类和…

【ARM】Day1

作业1&#xff1a;思维导图 作业2&#xff1a; 作业3&#xff1a;用for循环实现1~100之间和5050

ROS-PyQt小案例

前言&#xff1a;目前还在学习ROS无人机框架中&#xff0c;&#xff0c;&#xff0c; 更多更新文章详见我的个人博客主页【前往】 ROS与PyQt5结合的小demo&#xff0c;用于学习如何设计一个界面&#xff0c;并与ROS中的Service和Topic结合&#xff0c;从而控制多个小乌龟的运动…