深入解剖线程池(ThreadPoolExecutor)

news2024/12/29 11:08:04

目录

  • 1 线程池(ThreadPoolExecutor)
  • 2 线程池的使用(Executors)
    • 2.1 newFixedThreadPool
    • 2.2 newCachedThreadPool
    • 2.3 newSingleThreadExecutor
    • 2.4 newScheduledThreadPool

多线程应用程序中,线程的创建和销毁开销相对较高。每次创建线程都需要操作系统分配资源,销毁线程也需要释放资源。线程池就是为了解决这个问题,如果某个线程执行完自己的“任务”之后,并不是将线程释放,而是放到一个“池子”中,下次如果需要用到线程继续执行任务的话直接从池子中取,这样就不用再去重复的创建销毁。

1 线程池(ThreadPoolExecutor)

Java中线程池的底层真正实现是通过ThreadPoolExecutor来实现的,在使用的时候提供了Executors类供开发者使用,并提供了一系列的功能线程池。所以先来了解ThreadPoolExecutor的底层代码,然后再来分析Executors。

通过追源码,ThreadPoolExecutor的构造方法如下:

    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;
    }

这是官方API文档的描述
在这里插入图片描述可以看到构造方法中有以下这些参数,参数有的是必需的,有的是可选的,现在理解一下这些参数:

必需参数

  • corePoolSize - 即使空闲时仍保留在池中的线程数(核心线程数),除非设置allowCoreThreadTimeOut
  • maximumPoolSize - 池中允许的最大线程数
  • keepAliveTime -当线程数大于核心时,多余的空闲线程在终止之前等待新任务的最大时间
  • unit - keepAliveTime参数的时间单位
  • workQueue - 在执行任务之前用于保存任务的队列。该队列将仅保存execute方法提交的Runnable任务,采用阻塞队列实现。(这里使用的阻塞式队列有有界和无界两种形式,对于有界阻塞队列来说,达到饱和的时候就会触发拒绝策略,但是当使用的是无界队列的时候就永远不可能“满”也就不会触发拒绝策略。)

可选参数

  • threadFactory:线程工厂。用于指定为线程池创建新线程的方式。
  • handler :拒绝策略。当达到最大线程数时需要执行的饱和策略。

当线程池的线程数达到最大线程数时,需要执行拒绝策略。
Executors 为我们实现了 4 种拒绝策略:

  1. AbortPolicy():超过负荷,直接抛出异常(默认策略).
  2. CallerRunsPolicy():调用者负责处理.
  3. DiscardOldestPolicy():丢弃队列中最早的任务,将新任务加入.
  4. DiscardPolicy():丢弃新来的任务.

简单描述一下线程池的工作原理,对上面的参数有具体的认识:
在这里插入图片描述代码示例

public class Demo1 {
    public static void main(String[] args) {
    	//创建线程池(传入参数)
        ExecutorService pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS,
                new SynchronousQueue<Runnable>(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        //向线程池提交任务
        for (int i = 0; i < 3; i++) {
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello");
                }
            });
        }
    }
}

由于线程大于了线程池最大线程数,可以看到代码触发了拒绝策略,我们使用的是AbortPolicy(),所以超负荷就直接抛出异常。将循环中的3减少为2 就不会抛出异常。
在这里插入图片描述

2 线程池的使用(Executors)

Java对ThreadPoolExecutors进行了封装,方便使用者对线程池进行使用,Executors根据不同的使用场景提供了下面四种创建线程池的方式:

  • newFixedThreadPool: 创建固定线程数的线程池 定长线程池
  • newCachedThreadPool: 创建线程数目动态增长的线程池. 可缓存线程池
  • newSingleThreadExecutor: 创建只包含单个线程的线程池. 单线程化线程池
  • newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令, 是进阶版的 Timer.定时线程池。

具体地

2.1 newFixedThreadPool

  • 源码:
    在这里插入图片描述在这里插入图片描述
  • 特点:
    固定大小的线程池
    包含核心线程和最大线程数都为n,即corePoolSize和maximumPoolSize均为n。
    阻塞队列没有大小限制。(这里对于keppalivetime参数为0的设置,我看见有很多博客将这里会对线程执行完任务后立马回收停止,我认为是不正确的,官方API对于这个参数的解释是
    在这里插入图片描述当线程数大于核心时,多余的空闲线程在终止之前等待新任务的最大时间,。默认情况下,线程池只会回收非核心线程,如果希望核心线程也要回收,可以设置allowCoreThreadTimeOut这个属性为true,一般情况下我们不会去回收核心线程。因为线程池本身就是实现线程的复用,而且这些核心线程在没有任务要处理的时候是处于阻塞状态并没有占用CPU资源。
  • 使用场景:适用于需要限制并发线程数量的场景,例如控制同时执行的线程数量,以避免资源耗尽。

2.2 newCachedThreadPool

  • 源码
    在这里插入图片描述

  • 特点:
    动态大小的线程池。
    没有核心线程,核心线程数为0,最大线程数为Integer.MAX_VALUE。
    使用SynchronousQueue作为阻塞队列,可无限扩展。
    非核心线程的存活时间为60秒,即空闲60秒后的线程将被终止。

  • 使用场景:
    适用于需要处理大量短时任务的情况,例如任务执行时间不定,需要动态分配线程。

2.3 newSingleThreadExecutor

  • 源码
    在这里插入图片描述

  • 特点:
    包含单一核心线程的线程池。
    使用LinkedBlockingQueue作为阻塞队列。

  • 使用场景:

    适用于需要顺序执行任务的情况,保证任务按照提交的顺序依次执行

2.4 newScheduledThreadPool

  • 源码
    在这里插入图片描述

  • 特点:

    固定大小的线程池,包含corePoolSize个核心线程。
    使用DelayedWorkQueue作为阻塞队列,支持延迟执行和定时周期性执行。

  • 使用场景:

    适用于需要按计划执行任务、延迟执行或周期性执行任务的情况,例如定时任务和调度任务

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

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

相关文章

新手选MT4老手选MT5,有道理吗?anzo capital昂首资本这样分析

最近anzo capital昂首资本一直在分享MT4和MT5的常识&#xff0c;发现真的评论区惊现大神&#xff0c;竟直接作出总结&#xff1a;新手选MT4老手选MT5。不管正确与否&#xff0c;但是这些真的很炸裂啊&#xff01; 首先在指标和分析对象上&#xff0c;MT4有30个内置指标和33个分…

霸榜SPC-1,成立才3年多,这家中国的存储初创公司凭什么?

史书云&#xff1a;分久必合&#xff0c;合久必分。 高端存储“软硬件解耦”大势所趋。 【全球存储观察 &#xff5c; 热点关注】 提及SPC-1&#xff0c;在存储行业&#xff0c;可谓无人不知&#xff0c;无人不晓。 然而&#xff0c;对于一家成立于2020年1月19日&#xff0c…

2023年09月 C/C++(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C编程&#xff08;1~8级&#xff09;全部真题・点这里 Python编程&#xff08;1~6级&#xff09;全部真题・点这里 第1题&#xff1a;日期输出 给定两个整数&#xff0c;表示一个日期的月和日。请按照"MM-DD"的格式输出日期&#xff0c;即如果月和日不到2位时&…

Arduino是如何实现打开串口时,程序复位的?

引言 使用过Arduino的都应该发现过&#xff0c;在打开Arduino IDE自带的COM串口助手的时候&#xff0c;Arduino开发板会复位。在打开其他串口助手的时候也会。这是为什么呢&#xff1f; 原因分析 首先看看arduino的原理图&#xff1a; 我们可以看看与复位引脚相关的部…

【EI会议征稿】第四届材料化学与复合材料国际学术会议(MCCM 2023)

第四届材料化学与复合材料国际学术会议&#xff08;MCCM 2023&#xff09; 2023 4th International Conference on Material Chemistry and Composite Materials (MCCM 2023) 第四届材料化学与复合材料国际学术会议&#xff08;MCCM 2023&#xff09;将于2023年12月22-24日…

shiro550复现环境搭建

前言 Shiro反序列化漏洞指的是Apache Shiro安全框架中的一个潜在漏洞&#xff0c;该漏洞可能导致攻击者能够通过精心构造的恶意序列化对象来执行任意代码或进行拒绝服务&#xff08;DoS&#xff09;攻击。 这种漏洞的根源是在Shiro的RememberMe功能中&#xff0c;当用户选择“…

一个简单算法解决集群定时任务重复执行

好久不见&#xff0c;今天给大家分享一个非常好用的方法&#xff0c;来解决集群定时任务如何避免重复执行的问题。 需求描述&#xff1a; 大家都知道&#xff0c;定时任务在我们实际项目当中是会经常被使用到的&#xff0c;在生产环境中&#xff0c;为了保证服务的高可用&…

做一个物联网的后台程序与数据库设计

数据库部分 先设计一个简单的数据库。表结构如下: sql语句如下: SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0;-- ---------------------------- -- Table structure for realtimedata -- ---------------------------- DROP TABLE IF EXISTS `realtimedata`

【pycharm】sqlite3:Driver class ‘org.sqlite.JDBC‘ not found

pycharm 连接sqlite3的时候&#xff0c;点击&#xff1a;Test Connection 提示&#xff1a;Driver class ‘org.sqlite.JDBC’ not found. 点击&#xff1a;Download missing driver files 点击&#xff1a;Test Connection 这样就ok了&#xff1b;

执行make menuconfig问题的解决

执行make menuconfig 出现问题 在终端输入以下命令执行。 make menuconfig在终端输入上面命令执行时&#xff0c;没有成功运行&#xff0c;出现了如下的问题。 出现这个错误提示意味着在运行 make menuconfig 命令时&#xff0c;系统找不到 ncurses 库。ncurses 是一种文本用…

Python系列:彩色日志详解

前言 在使用Python进行编程时&#xff0c;我相信很多小伙伴都有一个共通的习惯&#xff0c;就是不管在什么场景下&#xff0c;都会先使用print()将我们获取到的数据打印出来以此来确保自己的代码没有问题。虽说在pycharm中提供了DEBUG功能&#xff0c;但是还是有很多小伙伴用不…

你知道期权和期货有什么关联和区别吗?解析一下!

随着金融市场的不断发展&#xff0c;期权和期货成为了两种常见的金融衍生品&#xff0c;它们都具有规避风险和发现价格的功能。很多入门不知道期权和期货哪个更好&#xff0c;下文科普你知道期权和期货有什么关联和区别吗&#xff1f;解析一下&#xff01;本文来源&#xff1a;…

AI Web3 盛会「EDGE」在港闭幕,融云国际影响力持续提升

&#x1f446;点击报名「爱嗨游」线上发布会 9 月 25 日至 27 日&#xff0c;全球规模最大的 AI & Web3 投资盛会——EDGE 峰会在中国香港举办&#xff0c;融云作为战略合作伙伴与来自世界各地的企业家、投资人、学者和热门项目方一同完成了这一盛会。关注【融云全球互联网…

虾皮商品评论数据接口,shopee商品评论API接口,虾皮API接口

虾皮商品评论数据接口的申请步骤如下&#xff1a; 在开放平台注册账号&#xff0c;申请API调用权限。登录开放平台&#xff0c;进入API管理界面&#xff0c;在接口列表中找到“商品详情”API接口&#xff0c;点击“申请使用”。填写API申请表格&#xff0c;包括API调用用途、申…

边端小场景音视频流分发架构

备注&#xff1a;绿色线条&#xff0c;红色线条&#xff0c;蓝色线条&#xff0c;均是表示同一路流的不同的协议而已 1&#xff09;IPC本身的流媒体的能力有限&#xff0c;一般IPC支持的客户端数10~50个&#xff0c;媒体分发能力&#xff1a;10~20路&#xff0c;看设备品牌能力…

AQS简介

AQS 简介 AQS&#xff08;AbstractQueuedSynchronizer&#xff09;是Java中用于实现同步器的抽象基类。它提供了一种灵活的方式来创建自定义的同步器&#xff0c;如锁、信号量等。AQS的底层包含了一些重要的成员变量&#xff0c;其中最重要的是state&#xff0c;用于表示同步…

2.连接Cesium Ion

1.登录Cesium 打开菜单上的 Cesium->Cesium. 点击 Connect To Cesium ion,进入网站进行登录以及授权。 2.创建Token 点击Token按钮&#xff0c;弹出Token选择界面&#xff0c;如果没有Token则创建一个&#xff0c;有的话可以直接进行选择。至此&#xff0c;我们可以在Unity中…

【electron】手把手教你在 electron 环境下使用 vue-devtools,附详细报错解决办法

▒ 目录 ▒ &#x1f6eb; 问题描述环境 1️⃣ vue-devtools问题来源解决思路 2️⃣ 屏蔽electron-devtools-installer加载的代码3️⃣ 加载本地vue-devtools插件代码实现electron加载本地插件获取vue-devtools&#xff1a;【推荐】Edge下载最新插件获取vue-devtools&#xff1…

Linux Shell 实现一键部署vmtools

VMware Tools 简介 VMware Tools 中包含一系列服务和模块&#xff0c;可在 VMware 产品中实现多种功能&#xff0c;从而使用户能够更好地管理客户机操作系统&#xff0c;以及与客户机操作系统进行无缝交互。 VMware Tools 具备以下功能&#xff1a; 将消息从主机操作系统传递…

2023年中国家用照明市场发展趋势分析:家用照明市场中智能产品提升显著[图]

2016-2019年我国家用照明行业迎来初步快速发展&#xff0c;2020-2021年受疫情影响增速稍微放缓&#xff0c;2022年&#xff0c;我国家用照明行业规模为362.8亿元&#xff0c;同比增长0.58%&#xff1b;预计2023年家用照明行业表现将有明显反弹&#xff0c;市场规模将达413亿元&…