【并发编程】线程池

news2024/11/17 8:40:50

背景

线程的创建和销毁都需要很大的开销,当线程数量过大,并且线程生命周期短。这时候线程频繁地创建和销毁就很没有必要。

在 Java 中可以通过线程池来解决此问题。线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用

在 JDK5 之前,我们必须手动实现自己的线程池,从 JDK5 开始,Java 内置支持线程池。在 JDK5 版本中增加了内置线程池实现 ThreadPoolExecutor,同时提供了Executors 来创建不同类型的线程池。Executors 中提供了以下常见的线程池创 建方法:

newSingleThreadExecutor:一个单线程的线程池。如果因异常结束,会再创建一个新的,保证按照提交顺序执行。

newFixedThreadPool:创建固定大小的线程池。根据提交的任务逐个增加线程,直到最大值保持不变。如果因异常结束,会新创建一个线程补充。

newCachedThreadPool:创建一个可缓存的线程池。会根据任务自动新增或回收线程。

虽然在 JDK 中提供 Executors 类来支持以上类型的线程池创建,但通常情况下不建议开发人员直接使用(见《阿里巴巴 java 开发规范》并发处理)。

线程池优点:

  • 重复利用线程,降低线程创建和销毁带来的资源消耗 。

  • 统一管理线程,线程的创建和销毁都由线程池进行管理 。

  • 提高响应速度,线程创建已经完成,任务来到可直接处理,省去了创建时间。

ThreadPoolExecutor 类

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

核心源码

public class ThreadPoolExecutor extends AbstractExecutorService {
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
    
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }
    
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }
    
    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:核心池的大小。在创建了线程池后,默认情况下,在创建了线程池后,线程池中的线程数为 0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到 corePoolSize 后,就会把到达的任务放到缓存队列当中;除非调用了prestartAllCoreThreads()或者 prestartCoreThread()方法,在没有任务到来之前就创建corePoolSize 个线程或者一个线程。

maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;

keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于 corePoolSize 时,keepAliveTime 才会起作用,直到线程池中的线程数不大于 corePoolSize,即当线程池中的线程数大于 corePoolSize 时,如果一个线程空闲的时间达到 keepAliveTime,则会终止,直到线程池中的线程数不超过 corePoolSize。

unit:参数 keepAliveTime 的时间单位,有 7 种取值,在 TimeUnit 类中有 7种静态属性:

TimeUnit.NANOSECONDS;          //纳秒
​
TimeUnit.MICROSECONDS;         //微秒
​
TimeUnit.MILLISECONDS;         //毫秒
​
TimeUnit.SECONDS;              //秒
​
TimeUnit.MINUTES;              //分钟
​
TimeUnit.HOURS;                //小时
​
TimeUnit.DAYS;                 //天

workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很

重要,会对线程池的运行过程产生重大影响

threadFactory:线程工厂,主要用来创建线程;

handler:表示当拒绝处理任务时的策略 ;

线程池的执行

创建完成 ThreadPoolExecutor 之后,当向线程池提交任务时,通常使用 execute 方法。execute 方法的执行流程图如下:

  1. 如果线程池中存活的核心线程数小于线程数 corePoolSize 时,线程池会创建一个核心线程去处理提交的任务。

  2. 如果线程池核心线程数已满,即线程数已经等于 corePoolSize,一个新提交的任务,会被放进任务队列workQueue 排队等待执行。3. 当线程池里面存活的线程数已经等于 corePoolSize 了,并且任务队列workQueue也满,判断线程数是否达到 maximumPoolSize,即最大线程 数是否已满,如果没到达,创建一个非核心线程执行提交的任务。

  3. 如果当前的线程数达到了 maximumPoolSize,还有新的任务过来的话,直接采用拒绝策略处理。

线程池中的队列

线程池有以下工作队列:

ArrayBlockingQueue:是一个用数组实现的有界阻塞队列,创建时必须设置长度,,按 FIFO 排序量。

LinkedBlockingQueue:基于链表结构的阻塞队列,按 FIFO 排序任务,容量可以选择进行设置,不设置是一个最大长度为 Integer.MAX_VALUE;

线程池的拒绝策略

构造方法的中最后的参数 RejectedExecutionHandler 用于指定线程池的拒绝策略。当请求任务不断的过来,而系统此时又处理不过来的时候,我们就需要采取对应的策略是拒绝服务。

默认有四种类型:

AbortPolicy 策略:该策略会直接抛出异常,阻止系统正常工作。

CallerRunsPolicy 策略:只要线程池未关闭,该策略在调用者线程中运行当前的任务(如果任务被拒绝了,则由提交任务的线程(例如:main)直接执行此任务)。

DiscardOleddestPolicy 策略:该策略将丢弃最老的一个请求,也就是即将被执行的任务,并尝试再次提交当前任务。

DiscardPolicy 策略:该策略丢弃无法处理的任务,不予任何处理。

execute 与 submit 的区别

执行任务除了可以使用 execute 方法还可以使用 submit 方法。

它们的主要区别是:execute 适用于不需要关注返回值的场景,submit 方法适用于需要关注返回值的场景。

关闭线程池

关闭线程池可以调用 shutdownNow 和 shutdown 两个方法来实现。

shutdownNow:对正在执行的任务全部发出 interrupt(),停止执行,对还未开始执行的任务全部取消,并且返回还没开始的任务列表。

shutdown:当我们调用 shutdown 后,线程池将不再接受新的任务,但也不会去强制终止已经提交或者正在执行中的任务

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

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

相关文章

[Java] 观察者模式简述

模式定义&#xff1a;定义了对象之间的一对多依赖&#xff0c;让多个观察者对象同时监听某一个主题对象&#xff0c;当主题对象发生变化时&#xff0c;他的所有依赖者都会收到通知并且更新 依照这个图&#xff0c;简单的写一个代码 package Section1.listener;import java.ut…

枚举类型

enum 枚举类型名 {命名枚举常量列表}; enum DAYS {MON, TUE, WED, THU, FRI, SAT, SUN};

POLARDB IMCI 白皮书 云原生HTAP 数据库系统 一 数据压缩和打包处理与数据更新

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

【报错】sqli-labs靶场搭建出现“Unable to connect to the database: security”

问题描述 搭建sqli-labs靶场时&#xff0c;在配置好PHP和mysql以及db-creds.inc配置文件后 初始界面可以运行&#xff0c;但点击关卡报错 提示连接不到数据库 Unable to connect to the database: security解决方案&#xff1a; 1、数据库配置出错&#xff0c;先查看db-cre…

ad+硬件每日学习十个知识点(11)23.7.22

文章目录 1.怎么使用quartus2编译工程生成sof文件&#xff1f;2.怎么使用quartus2下载程序到fpga芯片&#xff1f;3.为什么sof文件烧录后&#xff0c;fpga断电重启&#xff0c;程序会丢失&#xff1f;4.怎么使用quartus2把sof文件转换成jic文件&#xff1f;5.quartus2分配引脚的…

臻图信息以数字孪生赋能智慧文旅新发展

随着AI、元宇宙的热度持续攀升&#xff0c;以及中共中央办公厅、国务院办公厅此前印发了《“十四五”文化发展规划》、《关于推进实施国家文化数字化战略的意见》等重要文件&#xff0c;要求各地区各部门发挥好对产业的引导、扶持和监管作用。 数字孪生作为元宇宙建设的基石&am…

Pytorch个人学习记录总结 玩俄罗斯方块の深度学习小项目

目录 前言 模型成果演示 训练过程演示 代码实现 deep_network tetris test train 前言 当今&#xff0c;深度学习在各个领域展现出了惊人的应用潜力&#xff0c;而游戏开发领域也不例外。俄罗斯方块作为经典的益智游戏&#xff0c;一直以来深受玩家喜爱。在这个项目中&…

2、HAproxy调度算法

HAProxy的调度算法可以大致分为以下几大类&#xff1a; 静态算法&#xff1a;这类算法的调度策略在配置时就已经确定&#xff0c;并且不会随着负载的变化而改变。常见的静态算法有&#xff1a; Round Robin(轮询) Least Connections(最少连接数) Static-Weight(静态权重) Sourc…

Java16,执行tomcat的startup.bat脚本时一闪而过

win10 JDK16 tomcat-9.0.27 只需两步即可。。并没有其他那些文章说的那么多&#xff0c;什么JRE_HOME了&#xff0c;CLASSPATH了&#xff0c;&#xff0c;完全是乱扯。。。在此之前我从来没有配置过java环境变量。要不是tomcat的启动需要JAVA_HOME&#xff0c;我估计最终我都…

cppcheck使用

cppcheck使用 cppcheck Linux下 cppcheck 是一个静态代码检查工具&#xff0c;支持c, c 代码&#xff1b;作为编译器的一种补充检查&#xff0c;cppcheck对产品的源代码执行严格的逻辑检查。 执行的检查包括&#xff1a; 自动变量检查 数组的边界检查 class类检查 过期的函数…

计算机图形学十二光线追踪原理及实现细节

Whitted-style&#xff08;递归式&#xff09;光线追踪原理及实现细节 摘要 本篇文章主要分两个部分&#xff0c;第一部分会从为什么需要从光线追踪入手&#xff0c;一步步介绍Whitted-style光线追踪的原理&#xff0c;第二部分会具体介绍一些光线追踪的细节&#xff0c;包括…

谷粒商城第六天-实现功能的前序工作(网关的配置 跨域配置)

目录 一、为什么要做这项工作 1.1 为什么要配置网关 1.2 为什么要使用网关统一配置跨域 二、网关配置 三、统一跨域配置 四、总结 一、为什么要做这项工作 1.1 为什么要配置网关 我们知道网关的作用其实主要就是进行路由的&#xff0c;也就是根据前端发送到网关的请求&…

无涯教程-jQuery - unbind()方法函数

unbind([type]&#xff0c;[fn])方法的作用与bind相反&#xff0c;它从每个匹配的元素中删除绑定事件。 unbind( [type], [fn] ) - 语法 selector.unbind( [type], [fn] ) 这是此方法使用的所有参数的描述- type - 一种或多种事件类型&#xff0c;以空格分隔。 fn …

【机器学习】基础知识点的汇总与总结!更新中

文章目录 一、监督学习1.1、单模型1.1.1、线性回归1.1.2、逻辑回归&#xff08;Logistic Regression&#xff09;1.1.3、K近邻算法&#xff08;KNN&#xff09;1.1.4、决策树1.1.5、支持向量机&#xff08;SVM&#xff09;1.1.6、朴素贝叶斯 1.2、集成学习1.2.1、Boosting1&…

本地文件夹上传到Github

本地文件夹上传到Github 步骤1. 下载git步骤2. 在github中新建一个库&#xff08;Repository&#xff09;步骤3. 设置SSH key步骤4. 添加SSH keys步骤5. 本地文件上传到github参考 步骤1. 下载git 下载git客户端&#xff0c;并在本地安装完成。 步骤2. 在github中新建一个库&a…

解决 Windows 11 原生输入法卡顿问题

文章目录 词库损坏问题方法1. 删除个人词库方法2. 删除中文词库 网络延迟问题方法3&#xff1a;关闭云服务 资源调度问题方法4&#xff1a;调整优先级 升级兼容问题方法5&#xff1a;关闭兼容性&#xff08;针对 Win10 升级 Win11 的部分用户&#xff09; 终极大招 不知道有没有…

嵌入式数据库之SQLite

1.SQLite简介 轻量化&#xff0c;易用的嵌入式数据库&#xff0c;用于设备端的数据管理&#xff0c;可以理解成单点的数据库。传统服务器型数据 库用于管理多端设备&#xff0c;更加复杂。 SQLite是一个无服务器的数据库&#xff0c;是自包含的。这也称为嵌入式数据库&#x…

项目2 | 负载均衡式在线OJ

啊我摔倒了..有没有人扶我起来学习.... &#x1f471;个人主页&#xff1a; 《 C G o d 的个人主页》 \color{Darkorange}{《CGod的个人主页》} 《CGod的个人主页》交个朋友叭~ &#x1f492;个人社区&#xff1a; 《编程成神技术交流社区》 \color{Darkorange}{《编程成神技术…

【Vue3】父子组件传参

1. 父组件给子组件传值 父组件App.vue <template><div>父级</div><waterFallVue :title"name"></waterFallVue> </template><script setup lang"ts"> import waterFallVue from ./components/waterFall.vue …

基于Autoencoder自编码的64QAM星座图整形调制解调通信系统性能matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1星座图整形 4.2自编码器 4.3基于Autoencoder的星座图整形调制解调模型 4.4 实现过程 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 .…