【Java多线程学习7】Java线程池技术

news2024/11/25 22:44:10

线程池技术

一、什么是线程池

线程池顾名思义是管理一组线程的池子。当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程不会立即销毁,而是等待下一个任务。

二、为什么要使用线程池? 线程池的作用?

  • 1、降低资源消耗:通过重复利用已创建的线程,降低线程创建和销毁造成的消耗。
  • 2、提高响应速度:当任务到达时,任务可以不需要等待线程的创建,就立即执行。
  • 3、提高了线程的可管理性:通过创建线程池可以对线程进行统一的分配、调优和监控。

三、如何创建线程池

方法一:通过ThreadPoolExecutor构造函数来创建线程池(推荐)。

public class TaskExecutor {
    //线程池核心线程数量
    public static final int CORE_POOL_SIZE = 10;

    //线程池最大线程数
    public static final int MAX_POOL_SIZE = 30;

    //当线程数大于核心线程数时,多余的空闲线程存活的最长时间
    public static final int KEEP_ALIVE_TIME = 100;

    //线程池等待队列
    public static final int BLOCKING_QUEUE_SIZE = 10000;

    //通过ThreadPoolExecutor构造函数来创建线程池
    public static final ThreadPoolExecutor PROCESS_EXECUTOR = new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAX_POOL_SIZE,
            KEEP_ALIVE_TIME,
            TimeUnit.MICROSECONDS,
            new LinkedBlockingDeque<Runnable>(BLOCKING_QUEUE_SIZE),
            new ThreadFactoryBuilder().setNameFormat("thread-pool-%d").build());
    

    public static void executor(Runnable task) {
        PROCESS_EXECUTOR.execute(task);
    }
}

方法二:通过Executor框架的工具类Executors来创建线程池

public class TaskExecuter {

    public static void main(String[] args) {
        //通过Executors工具类来创建线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);

        threadPool.execute(() -> {
            System.out.println("Hello wys");
        });
    }

}

问题思考:
在实际的开发中为什么推荐使用通过ThreadPoolExecutor构造函数的方式来创建线程池,而不推荐使用Executors工具类的方式来创建线程池呢?
答:
通过 ThreadPoolExecutor 构造函数的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险,因为我们通过使用ThreadPoolExecutor构造函数的方式去创建线程的话,可以指定线程池的核心参数,如:线程池核心线程的数量、线程池最大线程的数量、空闲线程的存活时间、任务队列、拒绝策略等,这样便于对线程池更好的统一的去管理,避免了资源耗尽的风险

另外,Executors 返回线程池对象的弊端如下:

  • FixedThreadPool 和 SingleThreadExecutor:使用的是无界的 LinkedBlockingQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
  • CachedThreadPool:使用的是同步队列 SynchronousQueue, 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。
  • ScheduledThreadPool 和 SingleThreadScheduledExecutor : 使用的无界的延迟阻塞队列DelayedWorkQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。

在这里插入图片描述

四、线程池核心参数

我们通过查看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;
    }

ThreadPoolExecutor3个重要参数:

  • corePoolSize : 核心线程的数量。任务队列未达到队列容量时,最大可以同时运行的线程数量。
  • maximumPoolSize : 最大线程数量。 任务队列已满时(任务队列中存放的任务达到队列容量的时候),当前可以同时运行的线程数量变为最大线程数。
  • workQueue任务队列。新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。

其他常见参数:

  • keepAliveTime:线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁;
  • unit : keepAliveTime 参数的时间单位。
  • threadFactory:executor创建新线程时候会用到。
  • handler:饱和策略。

五、线程的饱和策略

线程的饱和策略handler:如果当前同时允许的线程数量达到最大线程数量,并且任务队列也已经放满了任务时,线程池的饱和策略就会生效,ThreadPoolExecutor定义了一些策略:

  • ThreadPoolExecutor.AbortPolicy:丢弃新任务并抛出异常 (RejectedExecutionException)。AbortPolicy也是线程池默认的拒绝策略
  • ThreadPoolExecutor.CallerRunsPolicy:它不会抛弃任务,也不会抛出异常,而是将任务回退给调用者的线程来执行
  • ThreadPoolExecutor.DiscardPolicy:不处理新任务,直接丢弃。
  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃当前任务队列中存在最久的任务,提交新的任务并执行。

在这里插入图片描述

六、线程池常用的阻塞队列有哪些?

  • ArrayBlockingQueue:是基于数组实现的阻塞队列,在初始化已经开辟好空间,容量是固定的,会存在内存空间浪费和内存碎片的问题

  • LinkedBlockingQueue(无界队列):是基于链表实现的阻塞队列,当创建LinkedBlockingQueue时,可以指定队列容量,也可以不指定队列容量,如果指定了队列容量则为有界队列,如果未指定队列容量则默认为无界队列。如果为无界队列由于队列永远不会被放满(容量为Integer.MAX_VALUE),所以此时最多只能创建核心线程数的线程。

  • SynchronousQueue(同步队列):是一种没有容量的队列,每个插入操作一定要等待一个相应的删除操作(即任务放进队列后,被取出后才能继续放入),目的是保证对于提交的任务,如果有空闲线程,则使用空闲线程来处理;否则新建一个线程来处理任务。

  • DelayedWorkQueue(延迟阻塞队列):其内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用“堆”的数据结构,可以保证每次出对的任务都是当前队列中执行时间最靠前的。

七、线程池处理任务的流程

我们来介绍下,线程池处理任务的流程(即线程池执行execute的流程):
在这里插入图片描述

  • 1、如果当前运行的线程数小于核心线程数,那么当有新的任务到来后会新建一个线程来执行任务(懒加载核心线程)
  • 2、如果当前运行的线程数等于或大于核心线程数,并且线程状态为运行态,那么就把此任务放到任务队列等待执行
  • 3、如果向任务队列投放任务失败(任务队列已经满了),但是当前运行线程数是小于最大线程数的,那么就尝试创建非核心线程来执行任务。
  • 4、如果当前运行的线程数已经等于最大线程,新建线程会使当前运行的线程超出最大线程数,此时会调用饱和策略(RejectedHandler)拒绝当前任务。

八、如何给线程池命名

利用ThreadFactorBuilder来给线程池命名。即new ThreadFactorBuilder().setNameFormat().build();

ThreadFactory 接口是 Java 提供的一个创建线程的工厂接口,我们可以通过实现这个接口来定制线程的行为。ThreadFactory 接口里唯一的方法是 newThread(Runnable r),它会创建并返回一个新的线程对象。
在实现该接口时,可以通过重载 newThread() 方法来定制线程的名称。具体实现方式示例代码如下:

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                .setNameFormat("my-thread-pool-%d").build();
ExecutorService executorService = new ThreadPoolExecutor(
                10, 10, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(), namedThreadFactory);

九、如何设定线程池的大小

(一)背景

我们在上面的学习中了解到使用线程池技术可以提高任务的响应速度,那是不是就说明线程池中线程的数量越大越好呢?其实理性的人都知道并不是这样的,虽然我们可以将线程池的数量设定的很大,但处理任务的CPU资源是不变的,这样就会导致大量的上下文切换,从而增加线程的执行时间,影响了整体执行效率

(二)上下文切换

问题1:什么是上下文切换
1、上下文切换是指:在多线程环境下,CPU从一个线程切换到另一个线程时,保存当前线程的上下文信息,并加载另一个线程上下文信息的过程。
上下文信息指:线程在执行过程中自己的运行条件和状态,比如,程序计数器、栈信息、寄存器的值等(寄存器是指CPU内部一组高速存储单元,用于临时存储和操作数据)

2、多线程编程中一般线程的个数都大于 CPU 核的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换。概括来说就是:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间。所以,上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。

(三)如何设定线程池的大小

  • CPU 密集型任务(线程池设定大小N+1): 这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1。比 CPU核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
  • I/O 密集型任务(线程池设定2N或是更大): 这种任务应用起来,系统会用大部分的时间来处理 I/O 交互(即CPU大部分时间是空闲),而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。

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

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

相关文章

【大数据】Flink 详解(二):核心篇 Ⅲ

Flink 详解&#xff08;二&#xff09;&#xff1a;核心篇 Ⅲ 29、Flink 通过什么实现可靠的容错机制&#xff1f; Flink 使用 轻量级分布式快照&#xff0c;设计检查点&#xff08;checkpoint&#xff09;实现可靠容错。 30、什么是 Checkpoin 检查点&#xff1f; Checkpoint …

四化智造MES(API)与金蝶云星空对接集成派工作业打通生产订单新增

四化智造MES&#xff08;API&#xff09;与金蝶云星空对接集成派工作业打通生产订单新增 对接系统&#xff1a;四化智造MES&#xff08;API&#xff09; MES建立统一平台上通过物料防错防错、流程防错、生产统计、异常处理、信息采集和全流程追溯等精益生产和精细化管理&#x…

mybatis如何生成和执行动态sql

文章目录 1. 相关代码2. SQL 语句解析全流程2.1 涉及到的重要类2.2 解析标签2.2.1 \<include>2.2.2 \<selectKey>2.2.3 处理 SQL 语句 3. 获取真正执行的sql 1. 相关代码 package com.boge.mapper;import com.boge.pojo.User;import java.util.List;public interf…

情感资源日记Resource Diary

什么是 Resource Diary &#xff1f; Resource Diary 是自托管的情感资源日记&#xff0c;允许用户跟踪特定任务的情感反应以进行趋势识别。 按照作者的设计&#xff0c;对每个任务&#xff0c;通过 /- 号来表达任务完成后的情绪&#xff0c;等级从 0 到 5 &#xff0c;其中&am…

大语言模型之三 InstructGPT训练过程

大语言模型 GPT历史文章中简介的大语言模型的的发展史&#xff0c;并且简要介绍了大语言模型的训练过程&#xff0c;本篇文章详细阐述训练的细节和相关的算法。 2020年后全球互联网大厂、AI创业公司研发了不少AI超大模型&#xff08;百亿甚至千亿参数&#xff09;&#xff0c;…

2023年中国政务云行业发展概况及发展趋势分析:政务云由基础设施建设向云服务运营转变[图]

政务云是指运用云计算技术&#xff0c;统筹利用已有的机房、计算、存储、网络、安全、应用支撑、信息资源等&#xff0c;发挥云计算虚拟化、高可靠性、高通用性、高可扩展性及快速、按需、弹性服务等特征&#xff0c;为政府行业提供基础设施、支撑软件、应用系统、信息资源、运…

QGIS3.28的二次开发六:VS不借助QT插件创建UI界面

上一篇博客我们说了在VS中如何使用QT插件来创建UI界面&#xff0c;但是我们二次开发QGIS的第一篇博客就说了&#xff0c;最好使用OSGeo4W中自动下载的QT进行QGIS二次开发&#xff0c;这样兼容性是最好的&#xff0c;那么该如何在VS中不使用外部安装的QT以及QT的VS插件情况下进行…

使用 LangChain 构建 LLM 应用详细教程(附python代码演练)

介绍 欢迎来到语言处理的未来&#xff01;在一个语言是连接人与技术的桥梁的世界中&#xff0c;自然语言处理&#xff08;NLP&#xff09;的进步为我们带来了令人难以置信的机会。其中一个重要的进步是革命性的语言模型&#xff0c;即大型语言模型&#xff08;LLM&#xff09;&…

图解java.util.concurrent并发包源码系列——Condition条件等待队列深入详解

图解java.util.concurrent并发包源码系列——Condition条件等待队列深入详解 Condition的作用Condition的原理Condition源码Condition的定义和Condition对象的获取await方法addConditionWaiter方法unlinkCancelledWaiters方法 fullyRelease方法isOnSyncQueue方法checkInterrupt…

一台阿里云服务器怎么部署多个网站?以CentOS系统为例

本文阿里云百科介绍如何在CentOS 7系统的ECS实例上使用Nginx搭建多个Web站点。本教程适用于熟悉Linux操作系统&#xff0c;希望合理利用资源、统一管理站点以提高运维效率的用户。比如&#xff0c;您可以在一台云服务器上配置多个不同分类的博客平台或者搭建多个Web站点实现复杂…

为新手和非技术人员提供扩展Web网站提供一个升级指南

本指南总结了扩展的基本原则&#xff0c;从一台服务器扩展到能够服务数百万用户的Web应用程序。它面向在技术领域工作的新手和非开发人员。因此&#xff0c;如果您刚刚部署了您的多云平台VPN设置&#xff0c;那么本文并不适合您。 话不多说&#xff0c;那就让我们开始吧&#x…

STM32CubeMX工程配置说明

一、STM32CubeMX配置 1.1 设置时钟 单片机的时钟&#xff0c;相当于人的心跳。只要单片机工作&#xff0c;必须要开启时钟&#xff01; STM32单片机共有4个时钟来源&#xff1a; 名称缩写频率外部连接功能用途特性外部高速晶体振荡器HSE4~16MHz4~16MHz晶体 系统时钟/RTC成…

流水线时序调度之规避冲突

1 写在前面的&#xff1a; 其实略微一个大点的机器&#xff0c;一个测试流程需要若干个步骤&#xff0c;都可以用流水线的思维去看待它&#xff1b; 我之前也没往流水线的角度去考虑&#xff0c;那有些机器的时序调度是不好理解的&#xff0c;甚至计算个通量都很麻烦&#xff…

p5.js 视频播放指南

theme: smartblue 本文简介 在刚接触 p5.js 时我以为这只是一个艺术方向的 canvas 库&#xff0c;没想到它还支持视频文件和视频流的播放。 本文简单讲讲如何使用 P5.js 播放视频。 播放视频文件 p5.js 除了可以使用 video 元素播放视频外&#xff0c;还支持使用 image 控件播放…

Linux 终端操作命令(3)内部命令用法

Linux 终端操作命令 内部命令用法 A- alias NAME alias - Define or display aliases. SYNOPSIS alias [-p] [name[value] ... ] DESCRIPTION Define or display aliases. Without arguments, alias prints the list of aliases in the reusable form al…

创建MySQL数据库和创建表的详细步骤(navicat)

目录 一、介绍 二、操作步骤 &#xff08;一&#xff09;新建连接 &#xff08;二&#xff09;新建数据库 &#xff08;三&#xff09;新建表 插入数据测试 对字段进行增加或者修改 三、关于MySQL的其他文章&#xff08;额外篇&#xff09; 一、介绍 在创建数据库…

(统计学习方法|李航)第一章统计学习方法概论-一二三节统计学习及统计学习种类,统计学习三要素

目录 一&#xff0c;统计学习 1.统计学习的特点 2.统计学习的对象 3.统计学习的目的 4.统计学习的方法 5.统计学习方法的研究 6.重要性 二&#xff0c;统计学习的基本种类 1.监督学习 &#xff08;1&#xff09;输入空间&#xff0c;输出空间和特征空间 &#xff08…

sklearn机器学习库(一)sklearn中的决策树

sklearn机器学习库(一)sklearn中的决策树 sklearn中决策树的类都在”tree“这个模块之下。 tree.DecisionTreeClassifier分类树tree.DecisionTreeRegressor回归树tree.export_graphviz将生成的决策树导出为DOT格式&#xff0c;画图专用tree.export_text以文字形式输出树tree.…

成像镜头均匀性校正——360°超广角均匀校准光源

随着空间技术的不断发展&#xff0c;遥感仪器在对地观测、大气探测及海洋探测等方面的应用也不断拓展&#xff0c;以实现不同任务的观测精度。空间遥感仪器热控技术旨在保证遥感器各部件所需温度水平、温度梯度和温度稳定度&#xff0c;以满足遥感器高质量成像要求。 近年来我国…

ubuntu20.04磁盘满了 /dev/mapper/ubuntu--vg-ubuntu--lv 占用 100%

问题 执行 mysql 大文件导入任务&#xff0c;最后快完成了&#xff0c;查看结果发现错了&#xff01;悲催&#xff01;都执行了 两天了 The table ‘XXXXXX’ is full &#xff1f; 磁盘满了&#xff1f; 刚好之前另一个 centos 服务器上也出现过磁盘满了&#xff0c;因此&a…