Spring默认线程池SimpleAsyncTaskExecutor

Spring默认线程池SimpleAsyncTaskExecutor

简介

  • SimpleAsyncTaskExecutor,不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程,没有最大线程数设置。并发大的时候会产生严重的性能问题。
  • 在Java中创建线程并不便宜,线程对象占用大量内存,在大型应用程序中,分配和取消分配许多线程对象会产生大量内存管理开销。

类的介绍

  • 从接口实现上看,现实 Executor 接口。

  • 在这里插入图片描述

  • 在这里插入图片描述

  • 此类特点概括为以下几点:

  • 为每个任务启动一个新线程,异步执行它。

  • 支持通过“concurrencyLimit”bean 属性限制并发线程。默认情况下,并发线程数是无限的。

  • 注意:此实现不重用线程!

  • 考虑一个线程池 TaskExecutor 实现,特别是用于执行大量短期任务。

  • 看到这个介绍后,是不是细思恐极:创建新线程、无限、不重用。

  • 这是不是和我们印象中的的线程池不一样,可以说是相悖的,完美躲过线程池优势。

  • 线程池的优势:

    • 降低创建线程和销毁线程的性能开销。
    • 提高响应速度,当有新任务需要执行是不需要等待线程创建就可以立马执行。
    • 合理的设置线程池大小可以避免因为线程数超过硬件资源瓶颈带来的问题。
  • 再看下SimpleAsyncTaskExecutor 类的结构:

  • 在这里插入图片描述

  • 可能常用的方法就是

  • setConcurrencyLimit 设置允许的最大并行访问数,起到一定的资源节流作用。 默认-1 表示根本没有并发限制,即不启用资源节流。

  • setTaskDecorator 指定一个自定义TaskDecorator以应用于任何即将执行的Runnable 。主要用例是围绕任务调度设置一些执行上下文,或者未任务执行提供一些监控/统计。

  • execute

  • submit

示例、源码分析

  • 先来个示例,再从源码看执行的流程:

  • public static void main(String[] args) {SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor("simple-async-");//设置允许的最大并行访问数executor.setConcurrencyLimit(Runtime.getRuntime().availableProcessors() + 1);//设置装饰者,传递一些上下文executor.setTaskDecorator(runnable -> {SecurityContext context = SecurityContextHolder.getContext();return () -> {try {SecurityContextHolder.setContext(context);runnable.run();} finally {SecurityContextHolder.clearContext();}};});for (int i = 0; i < 10; i++) {executor.execute(() -> log.info("执行线程:{}", Thread.currentThread().getName()));}
    }
    
  • 结果:创建了10个线程。

  • 源码走起,从 execute 方法开始:

  • //限流主要实现
    private final SimpleAsyncTaskExecutor.ConcurrencyThrottleAdapter concurrencyThrottle = new SimpleAsyncTaskExecutor.ConcurrencyThrottleAdapter();
    private ThreadFactory threadFactory;
    //设置最大的线程数量
    public void setConcurrencyLimit(int concurrencyLimit) {this.concurrencyThrottle.setConcurrencyLimit(concurrencyLimit);
    }
    //是否开启了限流 限流数量大于0?
    public final boolean isThrottleActive() {return this.concurrencyThrottle.isThrottleActive();
    }
    /*** Executes the given task, within a concurrency throttle* if configured (through the superclass's settings).* @see #doExecute(Runnable)*/
    @Override
    public void execute(Runnable task) {//调用下面的execute方法,TIMEOUT_INDEFINITE = = Long.MAX_VALUE;execute(task, TIMEOUT_INDEFINITE);
    }/*** Executes the given task, within a concurrency throttle* if configured (through the superclass's settings).* <p>Executes urgent tasks (with 'immediate' timeout) directly,* bypassing the concurrency throttle (if active). All other* tasks are subject to throttling.* @see #TIMEOUT_IMMEDIATE* @see #doExecute(Runnable)*/
    @Override
    public void execute(Runnable task, long startTimeout) {Assert.notNull(task, "Runnable must not be null");Runnable taskToUse = (this.taskDecorator != null ? this.taskDecorator.decorate(task) : task);//如果设置了并发限制且startTimeout>0,TIMEOUT_IMMEDIATE = 0if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {//开启并发/限流将执行的Runable进行封装,执行完成调用final方法 当前数量--this.concurrencyThrottle.beforeAccess();doExecute(new ConcurrencyThrottlingRunnable(taskToUse));}//没有设置并发数else {doExecute(taskToUse);}
    }
    
  • 看下 isThrottleActive 方法:

  • /*** 返回此油门当前是否处于活动状态。* Return whether this throttle is currently active.* 如果此实例的并发限制处于活动状态,则为true* @return {@code true} if the concurrency limit for this instance is active* @see #getConcurrencyLimit()*/
    public boolean isThrottleActive() {return (this.concurrencyLimit >= 0);
    }
    
  • 进入if后调用 beforeAccess 和 doExecute方法:beforeAccess 主要就是进行并发控制。

  • 并发控制/限流处理其实就是在执行任务之前和之后对于当前线程数量进行统计。

  • 简单的通过 synchronized 和 wati and notify 达到控制线程数量的效果,从而实现限流的策略。

  • /*** 在具体子类的主要执行逻辑之前被调用。* To be invoked before the main execution logic of concrete subclasses.* 此实现应用并发限制* <p>This implementation applies the concurrency throttle.* @see #afterAccess()*/
    protected void beforeAccess() {// concurrencyLimit并发限制数量, NO_CONCURRENCY=0,如果并发限制为0则报错if (this.concurrencyLimit == NO_CONCURRENCY) {throw new IllegalStateException("Currently no invocations allowed - concurrency limit set to NO_CONCURRENCY");}// 并发限制>0if (this.concurrencyLimit > 0) {boolean debug = logger.isDebugEnabled();//上锁synchronized (this.monitor) {boolean interrupted = false;// 如果当时的并发数量大于并发限制数量-就是我们设置那个值while (this.concurrencyCount >= this.concurrencyLimit) {if (interrupted) {throw new IllegalStateException("Thread was interrupted while waiting for invocation access, " +"but concurrency limit still does not allow for entering");}if (debug) {logger.debug("Concurrency count " + this.concurrencyCount +" has reached limit " + this.concurrencyLimit + " - blocking");}try {// 则等待,一直等待,等到并发数量变小。this.monitor.wait();}catch (InterruptedException ex) {// Re-interrupt current thread, to allow other threads to react.Thread.currentThread().interrupt();interrupted = true;}}if (debug) {logger.debug("Entering throttle at concurrency count " + this.concurrencyCount);}// 并发数量增加this.concurrencyCount++;}}
    }
    
  • doExecute方法:看到这,就知道了,每次都会创建新线程去执行任务。

  • /*** 用于实际执行任务的模板方法。* Template method for the actual execution of a task.* 默认实现创建一个新线程并启动它。* <p>The default implementation creates a new Thread and starts it.* @param task the Runnable to execute* @see #setThreadFactory* @see #createThread* @see java.lang.Thread#start()*/
    protected void doExecute(Runnable task) {Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));thread.start();
    }
    
  • 到此,再回头看开始的设置代码:

  • executor.setConcurrencyLimit(Runtime.getRuntime().availableProcessors() + 1);
    
  • 并发数=Java虚拟机的可用的处理器数量+1,

  • 当需要创建的线程数量大于并发数时,会等待,等待有任务结束,才创建新线程。区别于其他线程池,就意味着没任务队列,没有最大线程,请求会一直等待,可能就会等待超时。

  • 如果我们不设置并发数量,那么每次就会直接创建新线程(无限创建)。物极必反。

  • 到此,应该是 SimpleAsyncTaskExecutor 有了较清晰了解了吧,慎用。

  • 如果是我,是不会在生产环境使用上使用的!

  • 其他相关源码,这里就不做过多赘述了:

  • //这里是对于Runable对象执行再次封装,在执行完毕后处理限流操作
    private class ConcurrencyThrottlingRunnable implements Runnable {private final Runnable target;public ConcurrencyThrottlingRunnable(Runnable target) {this.target = target;}public void run() {try {this.target.run();} finally {SimpleAsyncTaskExecutor.this.concurrencyThrottle.afterAccess();}}
    }
    

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springBoot整合Redis(一、Jedis操作Redis)

在springboot环境下连接redis的方法有很多&#xff0c;首先最简单的就是直接通过jedis类来连接&#xff0c;jedis类就相当于是redis的客户端表示。 但是因为现在比较常用的是&#xff1a;StringRedisTemplate和RedisTemplate&#xff0c;所以jedis只做简单的介绍。 一、Jedis…

Linux调用可执行程序:system()函数和execl函数

system()函数&#xff1a; system()函数是一个在C/C编程语言中的库函数&#xff0c;用于在操作系统中执行命令。 函数声明如下&#xff1a; int system(const char *command);该函数接受一个指向以空字符结尾的字符串的指针作为参数&#xff0c;该字符串包含要执行的命令。函…

【Linux进程】进程状态---进程僵尸与孤儿

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 1.进程排队2.进程状态…

RCE (Remote ????? execution) --->CTF

看这个标题就知道今天的内容不简单&#xff01;&#xff01;&#xff01;&#xff01; 那么就来讲一下我们的RCE吧 目录 ​编辑 1. &&#xff1f; |&#xff1f; ||&#xff1f; &&&#xff1f; 2.PHP命令执行函数&& ||"" 1."" &…

详解0.9寸OLED驱动开发(linux platform tree i2c 应用实例)

目录 概述 1 认识OLED 1.1 OLED简介 1.2 SSD1306介绍 1.3 0.9寸OLED模块介绍 1.4 i2c操作OLED时序 2 驱动代码实现 2.1 查看i2c总线下Device 2.2 OLED驱动接口 2.2.1 初始化函数 2.2.2 写指令函数 2.2.3 其他接口函数 2.3 完整驱动代码 3 编译 3.1 编写测试程序 …

【算法训练营】:周测5

考题10-5 题目描述 平面固定有一些全等的圆角矩形&#xff0c;不同的圆角矩形具有不同的位置和倾斜角。这些圆角矩形都通过将以原本四个直角处距离两条直角边均为 r&#xfffd; 的位置为圆心&#xff0c;半径为 r&#xfffd; 且与两条直角边相切的四分之一圆弧以外的区域裁剪…

Ansible script 模块 该模块用于将本机的脚本在被管理端的机器上运行。Ansible服务执行本机脚本

目录 过程首先&#xff0c;我们写一个脚本&#xff0c;并给其加上执行权限直接运行命令来实现在被管理端执行该脚本验证错误演示 过程 该模块直接指定脚本的路径即可 首先&#xff0c;我们写一个脚本&#xff0c;并给其加上执行权限 vim /tmp/df.sh编辑脚本内容 这个脚本内容…

Linux安装Mysql设置自启动失败,bugFailed to execute operation: No such file or directory

bug: [rootlocalhost mysql-5.7]# systemctl enable mysql.service Failed to execute operation: No such file or directory 出现bug原因&#xff1a; ①CentOS7的服务systemctl脚本存放在&#xff1a;/usr/lib/systemd/&#xff0c;有系统&#xff08;system&#xff09;和…

2024022502-数据库绪论

数据库绪论 数据管理的三个阶段 人工管理阶段 文件系统阶段 数据库系统阶段 基本术语 数据&#xff08;Data&#xff09; 计算机用来描述事物的记录&#xff08;文字&#xff0e;图形&#xff0e;图像&#xff0e;声音&#xff09;数据的形式本身并不能完全表达其内容&am…

基础复习(IDA调试器)

1.选择IDA调试后端 在顶部有一个下拉菜单&#xff0c;选择调试器后端位置 很多用户实际上使用的是Windows版本的IDA&#xff0c;该IDA可以直接调试Windows下32bit和64bit的程序 2.本地调试启动方法 载入IDA后&#xff0c;程序实际上在对程序内置的一个字符串进行base64解码…

【项目部署上线】宝塔部署前端Docker部署后端

【项目部署上线】宝塔部署前端&Docker部署后端 文章目录 【项目部署上线】宝塔部署前端&Docker部署后端1.安装依赖1.1 安装mysql1.2 安装Canal1.3 安装redis1.4 安装rabbitmq1.5 安装nacos 2. 部署前端3. 部署后端 1.安装依赖 1.1 安装mysql docker run -d -p 3306:3…

Java面试——锁

​ 公平锁&#xff1a; 是指多个线程按照申请锁的顺序来获取锁&#xff0c;有点先来后到的意思。在并发环境中&#xff0c;每个线程在获取锁时会先查看此锁维护的队列&#xff0c;如果为空&#xff0c;或者当前线程是等待队列的第一个&#xff0c;就占有锁&#xff0c;否则就会…