并发基础之线程池(Thread Pool)

目录

    • 前言
    • 何为线程池
    • 线程池优势
    • 创建线程池方式
      • 直接实例化ThreadPoolExecutor类
      • JUC Executors 创建线程池
    • 线程池挖掘
      • Executors简单介绍
      • ThreadPoolExecutor核心类
      • ThreadPoolExecutor 类构造参数含义
      • 线程池运行规则
      • 线程设置数量
    • 结语

前言

相信大家都知道当前的很多系统架构都要求高并发,所谓高并发(High Concurrency)就是系统通过设计满足多个请求并行的能力,如果非要通俗一点就是系统在单位时间要满足较高的QPS\TPS。那么,如何让系统满足这些高并发能力呢?满足高并发能力不仅仅是分布式解耦、读写分离、限流削峰、缓存、队列,当前还有我们代码编写层面的多线程运用,让单位时间尽可能快的完成业务功能以提升系统吞吐量,吞吐量上来了QPS/TPS自然会提升。所以,今天我们主要对并发基础之线程池简要说明。

何为线程池

线程池英文 Thread Pool,是一种线程处理形式,望文生义就是一个装满线程的池子。当我们需要处理任务时直接从线程池中抓取线程执行,从而减少创建线程开销,避免创建过多线程影响系统开销,也为了尽可能压榨资源提升系统运行效率。

线程池优势

使用线程池的优点我们可以总结为以下几点:
1、重复使用线程,避免频繁创建线程开销,提升系统性能;
2、提供定时调度、单线程、并发数量控制功能,方便实现具体业务场景;
3、灵活的并发线程数量控制,尽可能多的压榨资源提升系统效率,避免过多线程阻塞系统;
4、提供了线程监控功能,可以监控系统运行资源情况

创建线程池方式

直接实例化ThreadPoolExecutor类

直接实例化ThreadPoolExecutor类,传入自定义构造参数

private ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
        // 线程池核心池的大小
        1,
        // 线程池的最大线程数
        2,
        // 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间
        1,
        // 等待的时间单位
        TimeUnit.SECONDS,
        // 用来储存等待执行任务的队列
        new ArrayBlockingQueue<Runnable>(10),
        //线程工厂
        new ThreadPoolExecutor.DiscardOldestPolicy());

JUC Executors 创建线程池

Executors 类有很多创建线程池的构造方法,如:
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

其本质上还是调用的ThreadPoolExecutor 线程执行类的构造方法。

线程池挖掘

Executors简单介绍

Java JUC 包下Executors类提供了多种创建线程池的方法:
在这里插入图片描述

总的来说我们可以分为如下几种线程池类型:
1、Executors.newCachedThreadPool:创建一个可缓存的线程池,如果线程池的大小超过了需要,可以灵活回收空闲线程,如果没有可回收线程,则新建线程
2、Executors.newFixedThreadPool:创建一个定长的线程池,可以控制线程的最大并发数,超出的线程会在队列中等待
3、Executors.newScheduledThreadPool:创建一个定长的线程池,支持定时、周期性的任务执行
4、Executors.newSingleThreadExecutor:创建一个单线程化的线程池,使用一个唯一的工作线程执行任务,保证所有任务按照指定顺序(先入先出或者优先级)执行
5、Executors.newSingleThreadScheduledExecutor:创建一个单线程化的线程池,支持定时、周期性的任务执行
6、Executors.newWorkStealingPool:创建一个具有并行级别的work-stealing线程池

ThreadPoolExecutor核心类

上文已经讲述了我们创建线程池常见的几种方式,这些方式JUC下Executors都已经提供。那么,这些常用的方法是如何创建线程池的呢?我们先查看选择一个创建方式查看源码:

//Executors.newFixedThreadPool 创建定长线程池方式
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

查看源码可知,创建一个定长线程池的静态方法内部实例化了一个线程池执行类ThreadPoolExecutor,再次进入ThreadPoolExecutor类查看源码:

//ThreadPoolExecutor 线程池执行类的一个有参数构造方法
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}
// 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;
}

查看源码可知,创建线程池是本质上是实例化了一个ThreadPoolExecutor 线程执行类,且传入了多个构造参数。ThreadPoolExecutor 线程执行类内部此时仅仅是将这些配置参数赋值给这些变量,已备后续线程池执行时候对线程的创建、销毁、调用等操作。

根据线程池的使用场景,我选用excute() 执行方法进行源码解读:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        reject(command);
}

如源码所示,可知excute()方法主要做了如下三件事:
1、如果运行线程少于corePoolSize 核心线程会尝试启动一个新线程执行任务,并在addWorker方法中检验runState和workerCount以防止报警。
2、如果任务可以成功排队,也要检查是否应该新建一个线程。因为可能在上次检查后已有线程死亡或者线程池关闭,这个时候就需要回滚排队重新创建一个线程执行任务。
3、如果任务不能排队,我们应该尝试新增一个线程。如果新增线程失败我们应该知道已经关闭或者饱和,此时就会用拒绝策略提示用户

当然还有其他的一些方法如:submit()提交任务、shutdown()关闭线程池、shutdownNow()立即关闭线程池。这些源码也较为简单,可以自行阅读。

ThreadPoolExecutor 类构造参数含义

corePoolSize:核心线程数量
maximumPoolSize: 最大线程数量
keepAliveTime:空闲线程存活时间,当线程数量大于corePoolSize核心线程数,且这些线程处于空闲状态,你们超过这个存活时间的线程将被销毁
TimeUnit:线程时间单位
BlockingQueue:线程阻塞队列,我们可以根据自身情况传入喜欢的阻塞队列
ThreadFactory: 线程创建工厂
RejectedExecutionHandler:线程池拒绝策略,线程池根据传入的配置参数在特定情况下会触发拒绝策略,目前常用的拒绝策略有:
1、直接抛出异常,这也是默认的策略,实现类为AbortPolicy;
2、用调用者所在的线程来执行任务,实现类为CallerRunsPolicy;
3、丢弃队列中最靠前的任务并执行当前任务,实现类为DiscardOldestPolicy;
4、直接丢弃当前任务,实现类为DiscardPolicy。

线程池运行规则

线程池执行任务运行规则如下:
1、如果运行线程数小于 corePoolSize 核心线程数,无论核心线程是否空闲都会新建一个线程执行
2、如果运行线程数大于、等于 corePoolSize 核心线程数,小于 maximumPoolSize 最大线程数,如果BlockingQueue 阻塞队列已满则新建一个线程执行,如果没有满则放入阻塞队列等待空闲线程执行
3、如果运行线程数大于maximumPoolSize,且BlockingQueue 阻塞队列已满则会执行RejectedExecutionHandler 异常策略,默认是直接抛出异常
4、如果运行线程数据大于 corePoolSize 核心线程数量,且存在空闲线程的情况,空闲线程会在 keepAliveTime 存活时间超时被踢掉,直至线程数等于 corePoolSize 核心线程数量

线程设置数量

1、对于CPU密集型任务,需要尽量的压榨CPU,一般建议线程数量为 nCPU + 1
2、对于IO密集型任务,一般建议线程数量为 2nCPU

结语

线程池的灵活运用是多线程开发以满足高并发场景的一大利器,在开发高并发功能业务时候,应当合理使用多线程。特别是应当理解 ThreadPoolExecutor 核心类源码设计,以便于我们创建出适宜的线程池。水能载舟亦能覆舟,良好多线程运用可以提升系统吞吐量,滥用多线程也会导致异常情况的发生。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

echart图表之highcharts

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、HighCharts是什么&#xff1f;二、使用步骤1.引入库2.前端代码3.展现结果4.后台自动截图总结前言 提示&#xff1a;这里可以添加本文要记录的大概内容&…

linux kernel 5.0 inline hook框架

github:https://github.com/WeiJiLab/kernel-hook-framework 一、项目介绍 Usually we want to hack a kernel function, to insert customized code before or after a certain kernel function been called, or to totally replace a function with new one. How can we…

计算机图形学11:二维观察之多边形的裁剪

作者&#xff1a;非妃是公主 专栏&#xff1a;《计算机图形学》 博客地址&#xff1a;https://blog.csdn.net/myf_666 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录专栏推荐专栏系列文章序一、多边形的裁剪…

Activity工作流(三):Service服务

3. Service服务 所有的Service都通过流程引擎获得。 3.1 RepositoryService 仓库服务是存储相关的服务&#xff0c;一般用来部署流程文件&#xff0c;获取流程文件&#xff08;bpmn和图片&#xff09;&#xff0c;查询流程定义信息等操作&#xff0c;是引擎中的一个重要的服务。…

Anaconda配置Python新版本tensorflow库(CPU、GPU通用)的方法

本文介绍在Anaconda环境中&#xff0c;下载并配置Python中机器学习、深度学习常用的新版tensorflow库的方法。 在之前的两篇文章基于Python TensorFlow Estimator的深度学习回归与分类代码——DNNRegressor&#xff08;https://blog.csdn.net/zhebushibiaoshifu/article/detail…

【C++学习】日积月累——SLT中stack使用详解(1)

一、stack的相关概念 stack是一种容器适配器&#xff0c;专门用在具有后进先出的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提取操作&#xff1b; stack是作为容器适配器被实现的&#xff0c;容器适配器即是对特定类封装作为其底层的容器&#xff0c;并提…

图形视图框架的坐标

图形视图基于笛卡尔坐标系;项目在场景中的位置和几何图形由两组数字表示&#xff1a;X 坐标和 Y 坐标。使用未变换的视图观察场景时&#xff0c;场景上的一个单元由屏幕上的一个像素表示。 图形视图中有三种有效的坐标系&#xff1a; 项目坐标场景坐标视图坐标为了简化实现图形…

opencv仿射变换之获取变换矩阵

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm1011.2415.3001.5343哔哩哔哩欢迎关注…

JavaSe第10次笔记

1.Java中&#xff0c;static不能修饰局部变量。 2.构造代码块&#xff1a;可用于成员变量的赋值&#xff0c;但需要注意的是&#xff0c;构造代码块最先执行(比构造方法先)。 3.静态代码块(可用于静态成员变量赋值)&#xff1a;写法如下 static { 静态成员操作; } (比构造…

Python逆向及相关知识

今天第二次看见python字节码的逆向题&#xff0c;然后发现了一个介绍Python逆向的文章&#xff0c;所以把文章里的内容简单整理记录一下。 文章参考&#xff1a;https://www.cnblogs.com/blili/p/11799398.html Python运行原理&#xff1a; 一.什么是Python Python 是一种解…

ChatGPT加强版GPT-4面世,打工人的方式将被颠覆

&#x1f517; 运行环境&#xff1a;chatGPT&#xff0c;GPT-4 &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#…

推荐一款卸载软件的小工具-《UninstallToo》

目录 UninstallToo介绍 UninstallToo下载 UninstallToo使用 总结 UninstallToo介绍 Uninstall Tool 是一款可以用来替代“添加/删除程序”的工具。它允许您显示隐藏的安装程序&#xff0c;按名称过滤已安装程序的列表&#xff0c;强行写在程序&#xff0c;浏览注册表项目&a…

【Vue】Vue的安装

&#x1f3c6;今日学习目标&#xff1a;Vue3的安装 &#x1f603;创作者&#xff1a;颜颜yan_ ✨个人格言&#xff1a;生如芥子&#xff0c;心藏须弥 ⏰本期期数&#xff1a;第一期 &#x1f389;专栏系列&#xff1a;Vue3 文章目录前言Vue3安装独立版本CDN安装第一个Vue程序总…

元数据管理实践数据血缘

元数据管理实践&数据血缘 什么是元数据&#xff1f;元数据MetaData狭义的解释是用来描述数据的数据&#xff0c;广义的来看&#xff0c;除了业务逻辑直接读写处理的那些业务数据&#xff0c;所有其它用来维持整个系统运转所需的信息&#xff0f;数据都可以叫作元数据。比如…

训练自己的GPT2-Chinese模型

文章目录效果抢先看准备工作环境搭建创建虚拟环境训练&预测项目结构模型预测续写训练模型遇到的问题及解决办法显存不足生成的内容一样文末效果抢先看 准备工作 从GitHub上拉去项目到本地&#xff0c;准备已训练好的模型百度网盘&#xff1a;提取码【9dvu】。 gpt2对联训…

10.0自定义SystemUI下拉状态栏和通知栏视图(六)之监听系统通知

1.前言 在进行rom产品定制化开发中,在10.0中针对systemui下拉状态栏和通知栏的定制UI的工作开发中,原生系统的下拉状态栏和通知栏的视图UI在产品开发中会不太满足功能, 所以根据产品需要来自定义SystemUI的下拉状态栏和通知栏功能,首选实现的就是下拉通知栏左滑删除通知的部…

【12】SCI易中期刊推荐——计算机信息系统(中科院4区)

🚀🚀🚀NEW!!!SCI易中期刊推荐栏目来啦 ~ 📚🍀 SCI即《科学引文索引》(Science Citation Index, SCI),是1961年由美国科学信息研究所(Institute for Scientific Information, ISI)创办的文献检索工具,创始人是美国著名情报专家尤金加菲尔德(Eugene Garfield…

SpringCloudAlibaba配置中心: nacos-config

nacos-config配置中心 本项目代码与笔记已存放在Gitee仓库 地址&#xff1a; 代码&#xff0c;笔记 文章目录nacos-config配置中心1.1 快速开始1.2 搭建nacos-config服务1.3 Config相关配置1.3.1 支持profile粒度的配置示例&#xff1a;1.3.2 支持命名空间分类配置(按环境规类…

【批处理】- 批处理自动安装Mysql与Redis

前言 在全新环境中安装MySQL与Redis操作是挺麻烦的&#xff0c;于是就想使用脚本来自动安装&#xff0c;使用批处理进行一步到位的安装&#xff0c;后面还能使用工具进行打包成exe可执行文件&#xff0c;一键安装&#xff0c;最后能够更好的部署项目到windows系统的服务器。 …

READ: Large-Scale Neural Scene Rendering for Autonomous Driving

READ: Large-Scale Neural Scene Rendering for Autonomous Driving &#xff1a;面向自动驾驶的大规模神经场景绘制 门卷积 https://www.jianshu.com/p/09fc8490104d https://blog.csdn.net/weixin_44996354/article/details/117409438摘要&#xff1a;论文提出了一种大规模神…