【JUC】线程池ThreadPoolTaskExecutor与面试题解读

1、ThreadPoolTaskExecutor 创建线程池

从它的创建和使用说起,创建和使用的代码如下:
创建:

   ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(keepAliveSeconds);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setThreadNamePrefix("ExecutorPool-");
        executor.initialize();
        return executor;

使用:

 threadPoolTaskExecutor.execute(() -> {
                    //do something...
                });

上面创建线程池使用的是ThreadPoolTaskExecutor ,点击进入executor.initialize()方法:
ExecutorConfigurationSupport.java

	/**
	 * Set up the ExecutorService.
	 */
	public void initialize() {
		if (logger.isInfoEnabled()) {
			logger.info("Initializing ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : ""));
		}
		if (!this.threadNamePrefixSet && this.beanName != null) {
			setThreadNamePrefix(this.beanName + "-");
		}
		this.executor = initializeExecutor(this.threadFactory, this.rejectedExecutionHandler);
	}

再进入initializeExecutor方法:
ThreadPoolTaskExecutor.java

	/**
	 * Note: This method exposes an {@link ExecutorService} to its base class
	 * but stores the actual {@link ThreadPoolExecutor} handle internally.
	 * Do not override this method for replacing the executor, rather just for
	 * decorating its {@code ExecutorService} handle or storing custom state.
	 */
	@Override
	protected ExecutorService initializeExecutor(
			ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {

		BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);

		ThreadPoolExecutor executor;
		if (this.taskDecorator != null) {
			executor = new ThreadPoolExecutor(
					this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
					queue, threadFactory, rejectedExecutionHandler) {
				@Override
				public void execute(Runnable command) {
					Runnable decorated = taskDecorator.decorate(command);
					if (decorated != command) {
						decoratedTaskMap.put(decorated, command);
					}
					super.execute(decorated);
				}
			};
		}
		else {
			executor = new ThreadPoolExecutor(
					this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
					queue, threadFactory, rejectedExecutionHandler);

		}

		if (this.allowCoreThreadTimeOut) {
			executor.allowCoreThreadTimeOut(true);
		}

		this.threadPoolExecutor = executor;
		return executor;
	}
ThreadPoolExecutor  executor = new ThreadPoolExecutor(corePoolSize,
									 maxPoolSize,
									 keepAliveSeconds,
 									 TimeUnit.SECONDS,
									 queue, 
									 threadFactory,
									 rejectedExecutionHandler);

可以看到new ThreadPoolExecutor(),ThreadPoolTaskExecutor 本质是使用ThreadPoolExecutor,那为何要存在ThreadPoolTaskExecutor?
ThreadPoolTaskExecutor的方法基本都是围绕如何创建ThreadPoolExecutor,安全有效设置各种参数,添一些行为等。
在这里插入图片描述
如下面设置MaxPoolSize(最大线程池数),它考虑到并发同步、threadPoolExecutor 是否为null的问题。
ThreadPoolTaskExecutor.java

/**
 * Set the ThreadPoolExecutor's maximum pool size.
 * Default is {@code Integer.MAX_VALUE}.
 * <p><b>This setting can be modified at runtime, for example through JMX.</b>
 */
public void setMaxPoolSize(int maxPoolSize) {
	synchronized (this.poolSizeMonitor) {
		this.maxPoolSize = maxPoolSize;
		if (this.threadPoolExecutor != null) {
			this.threadPoolExecutor.setMaximumPoolSize(maxPoolSize);
		}
	}
}

再如:
设置队列时,它会创建一个安全的队列,有合适大小的LinkedBlockingQueue或者没有大小的SynchronousQueue。从而避免使用newSingleThreadExecutor这类创建一个不安全的等待队列。
ThreadPoolTaskExecutor.java

/**
 * Create the BlockingQueue to use for the ThreadPoolExecutor.
 * <p>A LinkedBlockingQueue instance will be created for a positive
 * capacity value; a SynchronousQueue else.
 * @param queueCapacity the specified queue capacity
 * @return the BlockingQueue instance
 * @see java.util.concurrent.LinkedBlockingQueue
 * @see java.util.concurrent.SynchronousQueue
 */
protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
	if (queueCapacity > 0) {
		return new LinkedBlockingQueue<>(queueCapacity);
	}
	else {
		return new SynchronousQueue<>();
	}
}

newSingleThreadExecutor方式创建的等待队列中的LinkedBlockingQueue容量是Integer.MAX_VALUE
Executors.java

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

LinkedBlockingQueue.java

 public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

ThreadPoolTaskExecutor的父类ExecutorConfigurationSupport的方法可以设置BeanName、ThreadNamePrefix等。
在这里插入图片描述
总结来说在spring项目中使用ThreadPoolTaskExecutor创建线程池是首推使用的。

2、ThreadPoolExecutor解读

2.1 基本使用介绍

ThreadPoolExecutor  executor = new ThreadPoolExecutor(corePoolSize,
									 maxPoolSize,
									 keepAliveSeconds,
 									 TimeUnit.SECONDS,
									 queue, 
									 threadFactory,
									 rejectedExecutionHandler);
  • corePoolSize:线程池的核心线程数,定义了最小可以同时运行的线程数量。

  • maximumPoolSize:线程池的最大线程数。队列中存放的任务达到队列容量时,可以同时运行的线程数量变为最大线程数。

  • keepAliveTime:当线程池中的线程数量大于corePoolSize时,如果没有新任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了KeepAliveTime才会被回收销毁。

  • unit:keepAliveTime参数的时间单位,包括DAYS、HOURS、MINUTES、MILLISECONDS等。

  • workQueue:用于保存等待执行任务的阻塞队列。可以选择以下集个阻塞队列:

    • ArrayBlockingQueue:是一个基于数组结构的阻塞队列,此队列按FIFO原则对元素进行排序;
    • LinkedBlockingQueue:是一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列;
    • SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量常高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool()使用了这个队列;
    • PriorityBlockingQueue:一个具有优先级的无限阻塞队列;
  • threadFactory:用于设置创建线程的工厂,可以通过工厂给每个创造出来的线程设置更有意义的名字。使用开源框架guava提供的ThreadFactoryBuilder可以快速给线程池里的线程设置有意义的名字:new ThreadFactoryBuilder().setNameFormat(“XX-task-%d”).build();7)handler:饱和策略。若当前同时运行的线程数量达到最大线程数量并且队列已经被放满,ThreadPoolExecutor定义了一些饱和策略:

    • ThreadPoolExecutor.AbortPolicy:直接抛出RejectedExecutionException异常来拒绝处理新任务;
    • ThreadPoolExecutor.CallerRunsPolicy:只用调用者所在的线程来运行任务,会降低新任务的提交速度,影响程序的整体性能;
    • ThreadPoolExecutor.DiscardPolicy:不处理新任务,直接丢弃掉;
    • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列中最近的一个任务,执行当前任务;

    处理流程:

在这里插入图片描述
1.在创建了线程池之后,等待提交过来的任务请求
2.当调用execute()方法添加一个请求任务的时候,线程池会做出如下判断:
2.1 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个程序
2.2 如果正在运行的线程数量大于或者等于corePoolSize,那么将这个任务放入队列
2.3 如果这个时候队列满了并且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻执行这个任务
2.4 如果队列满了并且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行
3. 当一个线程完成任务的时候,它会从队列中取下一个任务来执行
4. 当一个线程无事可做超过一定时间(keepAliveTime)时:线程会判断如果当前运行的线程数大于corePoolSize,那么这个线程就会被停掉。
最经典的比喻是银行办理业务,可以很形象的去理解线程池七个核心参数之间的关系:

在这里插入图片描述
银行刚开始上班,然后又一位需要办理业务的顾客1进来,柜员1就来一个帮这位顾客1办理,其他在吃早餐摸鱼。这位顾客1办理完了业务就走了,柜员1坐在柜台上静等。

这时又有一位顾客2过来,这时不是柜员1去接待,而是叫上柜员2,因为这天安排上班的两位柜员(corePoolSize=2)。

顾客3来办理业务,因为上班的柜员已经全部就位了,柜员1有空,柜员1就去接待顾客3。

顾客4来办理业务,因为柜员1和柜员2都在忙,所以顾客4在凳子上静等。柜员2帮顾客2办理完了,就去给顾客4办理。这时凳子又空出3张(Queue容量为3)。

顾客5、顾客6、顾客7同时过来,柜员1和柜员2都在忙,顾客5、顾客6、顾客7就坐满了凳子。

过了一会,顾客8来了,柜员1和柜员2在忙同时没有凳子坐了,本着顾客就是上帝的原则,打电话叫来一个在休息的柜员3帮顾客8办理业务。

生意兴隆,顾客9来了,打电话叫来一个在休息的柜员4帮顾客9办理业务。

这是顾客10来了,一进门,柜员没有了,连休息中的也没有了(maximumPoolSize=4),银行直接赶走他。

过了一段时间,银行的客户都办完业务走了,那两位本来应该休息的柜员说:我们再等一下(keepAliveTime),不忙了我们就撤了。

看上面的场景中,搬救兵请休息中的柜员回来干活是很麻烦的,要先坐满凳子再说。线程池中也是,要等等待队列满了以后才会去创建核心线程数之外的线程,因为创建线程的花销是很大的。

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

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

相关文章

使用PHP实现随机调用图片

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 预览地址&#xff1a;ht…

【CI/CD】基于 Jenkins+Docker+Git 的简单 CI 流程实践(上)

基于 JenkinsDockerGit 的简单 CI 流程实践&#xff08;上&#xff09; 在如今的互联网时代&#xff0c;随着软件开发复杂度的不断提高&#xff0c;软件开发和发布管理也越来越重要。目前已经形成一套标准的流程&#xff0c;最重要的组成部分就是 持续集成 及 持续交付、部署。…

深入探索JavaEE单体架构、微服务架构与云原生架构

课程链接&#xff1a; 链接: https://pan.baidu.com/s/1xSI1ofwYXfqOchfwszCZnA?pwd4s99 提取码: 4s99 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 --来自百度网盘超级会员v4的分享 课程介绍&#xff1a; &#x1f50d;【00】模块零&#xff1a;开营直播&a…

2023-08-15 linux mipi 屏幕调试:有一个屏幕开机时候不显示,开机后按power 按键休眠唤醒就可以显示。原因是reset gpio 被复用

一、现象&#xff1a;今天更新了一个新版本的buildroot linux sdk &#xff0c;调试两个mipi 屏幕&#xff0c;这两个屏幕之前在其他的sdk都调好了的&#xff0c;所有直接把配置搬过来。但是有一个屏幕可以正常显示&#xff0c;有一个屏幕开机时候不显示&#xff0c;开机后按po…

django-基本环境配置

文章目录 django 环境安装1. 安装环境1.1 安装 Python (配置虚拟环境)1.1.1 步骤 1.2 Conda配置环境参考 django 环境安装 1. 安装环境 1.1 安装 Python (配置虚拟环境) 由于国外源速度慢&#xff0c;可以pip添加清华源 pip config set global.index-url https://pypi.tuna.…

Jmeter进阶使用:BeanShell实现接口前置和后置操作

一、背景 我们使用Jmeter做压力测试或者接口测试时&#xff0c;除了最简单的直接对接口发起请求&#xff0c;很多时候需要对接口进行一些前置操作&#xff1a;比如提前生成测试数据&#xff0c;以及一些后置操作&#xff1a;比如提取接口响应内容中的某个字段的值。举个最常用…

Mysql之explain详解

1. explain作用 使用explain可以展示出sql语句的执行计划&#xff0c;再根据sql的执行计划去判断这条sql有哪些点可以进行优化&#xff0c;从而让sql的效率达到最大化。 2. 执行计划各列含义 &#xff08;1&#xff09;id&#xff1a;id列是select的序列号&#xff0c;这个…

关于APP备案、小程序备案的问题,如何备案?

近日&#xff0c;工信部发布了关于开展移动互联网应用程序备案工作的通知。为落实相关法律法规要求&#xff0c;促进互联网行业规范健康发展&#xff0c;进一步做好移动互联网信息服务管理&#xff0c;现组织开展移动互联网应用程序&#xff08;以下简称 APP&#xff09;备案工…

数据结构的图存储结构

目录 数据结构的图存储结构 图存储结构基本常识 弧头和弧尾 入度和出度 (V1,V2) 和 的区别,v2> 集合 VR 的含义 路径和回路 权和网的含义 图存储结构的分类 什么是连通图&#xff0c;&#xff08;强&#xff09;连通图详解 强连通图 什么是生成树&#xff0c;生…

计算机竞赛 opencv 图像识别 指纹识别 - python

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于机器视觉的指纹识别系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;4分 该项目较为新颖&#xff0c;适…

[oneAPI] 手写数字识别-GAN

[oneAPI] 手写数字识别-GAN 手写数字识别参数与包加载数据模型训练过程结果 oneAPI 比赛&#xff1a;https://marketing.csdn.net/p/f3e44fbfe46c465f4d9d6c23e38e0517 Intel DevCloud for oneAPI&#xff1a;https://devcloud.intel.com/oneapi/get_started/aiAnalyticsToolki…

【STM32】 工程

&#x1f6a9; WRITE IN FRONT &#x1f6a9; &#x1f50e; 介绍&#xff1a;"謓泽"正在路上朝着"攻城狮"方向"前进四" &#x1f50e;&#x1f3c5; 荣誉&#xff1a;2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2022博客之星TO…

html2canvas生成图片地址Base64格式转成blob在转成file(二进制)可正常发送(保姆教程,复制粘贴可用)

开始: 最终结果: 1. html2canvas方法生成的图片地址已Base64编码形式放在img标签src中可直接展示生成的图片(注意页面标签获取位置,还有个setTimeout页面渲染需要时间) setTimeout(function () {var result {};v…

DiffusionDet: Diffusion Model for Object Detection

DiffusionDet: Diffusion Model for Object Detection 论文概述不同之处整体流程 论文题目&#xff1a;DiffusionDet: Diffusion Model for Object Detection 论文来源&#xff1a;arXiv preprint 2022 论文地址&#xff1a;https://arxiv.org/abs/2211.09788 论文代码&#xf…

24、springboot的自动配置01--类条件注解@ConditionalOnClass、bean条件注解@ConditionalOnBean

条件注解的理解&#xff1a;该注解指定了一些条件&#xff0c;只有符合这些条件&#xff0c;被该注解修饰的类或方法才能生效。 这些条件可以是yml配置文件里面的属性等数据是否存在&#xff0c;也可以是一些依赖驱动是否存在的条件、也可以是指定的bean是否存在等。 springbo…

Golang协程,通道详解

进程、线程以及并行、并发 关于进程和线程 进程&#xff08;Process&#xff09;就是程序在操作系统中的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff0c;进程是一个动态概念&#xff0c;是程序在执行过程中分配和管理资源的基本单位&#xff0c;每一…

iTOP-RK3588开发板安装TFTP服务端

首先在 ubuntu 中执行以下命令安装 TFTP 服务&#xff1a; apt-get install tftp-hpa tftpd-hpa 安装完成以后创建 TFTP 服务器工作目录,并对 TFTP 的服务配置文件进行修改,具体步骤如下&#xff1a; 输入以下命令在家目录创建 tftpboot 文件夹&#xff0c;如下图所示&#x…

Prompt、RAG、微调还是重新训练?如何选择正确的生成式AI的使用方法

生成式人工智能正在快速发展&#xff0c;许多人正在尝试使用这项技术来解决他们的业务问题。一般情况下有4种常见的使用方法&#xff1a; Prompt EngineeringRetrieval Augmented Generation (RAG 检索增强生成)微调从头开始训练基础模型(FM) 本文将试图根据一些常见的可量化…

爬虫逆向实战(十七)--某某丁简历登录

一、数据接口分析 主页地址&#xff1a;某某丁简历 1、抓包 通过抓包可以发现数据接口是submit 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现有一个enPassword加密参数 请求头是否加密&#xff1f; 通过查看请求头可以发现有一个To…

C++学习系列之动态库报错问题

C学习系列之动态库报错问题 啰嗦问题解决总结 啰嗦 动态库已建&#xff0c;C文件一加&#xff0c;全是报错&#xff0c;一片红。 问题 解决 解决办法就是加标头 总结 小问题&#xff0c;记录一下。