线程池、及Springboot线程池实践

摘要

本文介绍了线程池基本概念、线程及线程池状态、java中线程池提交task后执行流程、Executors线程池工具类、最后介绍在springboot框架下使用线程池和定时线程池,以及task取消

线程池基本

背景

线程池

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务

线程池优势

  • 降低执行task重复创建销毁线程的消耗
  • 提高执行task响应速度。切换到task时,无需创建线程,直接将task“挂”到线程执行

线程状态

线程共有6个状态,分别是new、runnable、waiting、time_waiting、blocked、terminal。其中runnable状态包括running、ready。且runnable和waiting、time_waiting、blocked、terminal之间切换。

new

线程实例化初始状态

RUNNABLE(running、ready)

running:正在运行

ready:running状态线程占用cpu时间片完(主动调用yield)后状态。

yield():向调度程序提示当前线程愿意放弃cpu的使用,调度程序可以忽略此提示。很少使用,可用于调试,它可能有助于重现由于竞争条件而产生的错误。

waiting

等待状态,例如调用wait、join、park方法,线程等待

time_waiting

限时等待,例如调用sleep(time),wait(time),join(time),parkNanos(),parkUntil(thread)

blocked

阻塞状态,例如等待锁,或等待进入synchronized块

terminal

线程终止

线程池状态

RUNNING

可接收新提交的任务,可处理队列中任务。

SHUTDOWN

不再接收新提交的任务,但是可处理队列中的任务。

STOP

不再接收新提交的任务,且不再处理队列中的任务。

TIDYING

所有任务已经被终结,工作线程数为0,该状态会执行钩子函数terminated()

TERMINATED

已执行完毕terminated()方法。

状态转换

RUNNING -> SHUTDOWN:调用shutdown()方法后。

(RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()方法后。

SHUTDOWN -> TIDYING:当队列中任务为空,线程池中任务为空。

TIDYING -> TERMINATED:terminated()钩子方法执行完毕之后。

Java线程池实践

ThreadPool参数

corePoolSize,maximumPoolSize,workQueue,keepAliveTime,TimeUnit,threadFactory,RejectedExecutionHandler

核心线程数

corePoolSize:线程池中存活线程数量,除非allowCoreThreadTimeOut=true时会被kill

最大线程数

maximumPoolSize:线程池允许最大线程数,达到keepAliveTime后会被kill

等待时间和单位

keepAliveTime:多于核心线程之外的线程,超过该时间的线程会被kill

TimeUnit:keepAliveTime时间单位

阻塞队列

workQueue:保存未执行的Runnable 的task

线程工厂

threadFactory:创建线程的工厂类,默认Executors.defaultThreadFactory()

拒绝策略

RejectedExecutionHandler:因队列满、且超过最maxThreadPool限制的task处理策略

  • AbortPolicy:拒绝task,抛出RejectedExecutionException 。是ThreadPoolExecutor和ScheduledThreadPoolExecutor的默认拒绝策略
  • DiscardPolicy:丢弃task,无异常
  • DiscardOldestPolicy:丢弃未处理的最old的task,无异常
  • CallerRunsPolicy:拒绝task,抛回给提交task的线程执行
    static volatile int  count = 0;
    public static void main(String[]args){
//可接纳6+1=7个任务,再多则会执行默认拒绝策略
         ThreadPoolExecutor executor = new ThreadPoolExecutor(5,6,1000l, 
                TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(1));
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println(count++);
                try {
                    Thread.sleep(1000000000l);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        for(int i=0;i<8;i++){
            executor.execute(task);//抛出RejectedExecutionException
        }
        System.out.println(executor.getActiveCount());
    }

Task提交流程

coreThread  --  queue -- maxThread -- handler

  • 若运行线程少于corePoolSize,尝试以给task启动一个新的core线程
  • task进入BlockQueue排队
  • BlockQueue已满,添加新线程。失败则拒绝该task,执行拒绝策略

Thread回收

回收逻辑:Runnable状态线程数ctl减1;从工作线程HashSet中移除工作线程Worker对象

  • 回收超过keepAliveTime之外的非core线程
  • 回收超过keepAliveTime的core线程,且allowCoreThreadTimeOut=true

 线程池任务管理

获取活跃线程数

ThreadPoolExecutor.getActiveCount()。线程池中正在运行的线程个数,包括核心线程+队列满后新建的非核心线程

static volatile int  count = 0;
    public static void main(String[]args) throws InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,9,1000l,
                TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(1));
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println(count++);
                try {
                    Thread.sleep(1000000000l);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        for(int i=0;i<8;i++){
            executor.execute(task);
        }
//输出为7:5个在核心线程,1个在队列,2个为新建线程
        System.out.println("活跃线程:"+executor.getActiveCount());
    }

获取提交的task状态

ThreadPoolExecutor.getTaskCount():历史提交的task总数

ThreadPoolExecutor.getCompletedTaskCount():已完成task个数

取消任务

futrure.cancel()

task提交后状态

  • 线程未启动
  • 线程正在执行
  • 线程已结束

task未启动,cancel后会从线程池的阻塞队列中remove掉task;

task已结束,cancel对task不会有任何影响

task正在执行:通过interrupted标志位,在sleep/join/wait处抛出中断异常终止task执行

无future第三方库

 该场景适用于调用第三方库无future返回值情况。可以通过在第三方库中例如cancelTask等方法中重写Future.cancel()取消task

Java Executors线程池工具

Executors是java一个线程池工具,便捷创建例如单个、固定数量、定时等特征的线程池。不同特征的线程池,其参数不同。

newSingleThreadPool

适用于提交的任务按顺序执行场景:核心线程数=最大线程数=1,一直存活,如果任务异常中断了线程,则创建一个新线程;使用链表阻塞无界队列;默认线程工厂Executors.defaultThreadFactory();默认阻塞策略:AbortPolicy

短期提交任务过多,则队列溢出,内存溢出

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

cachedThreadPool

适用于执行许多短期(60s)异步任务场景:核心线程为0,最大线程为Integer.MAX_VALUE,存活60s,使用同步队列SynchronousQueue;默认线程工厂Executors.defaultThreadFactory();默认阻塞策略:AbortPolicy

短期提交任务过多,则一直创建线程,内存溢出

SynchronousQueue(同步队列):队列中不存储元素,元素的offer和take阻塞对应 

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}

newFixedThreadPool

适用于执行IO较少异步任务场景:核心线程=最大线程=输入参数,线程创建后不回收,使用LinkedBlockingQueue阻塞无界队列;默认线程工厂Executors.defaultThreadFactory();默认阻塞策略:AbortPolicy

短期提交任务过多,则队列溢出,内存溢出

   public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

newThreadScheduledExecutor

适用于单一定时任务:核心线程数=1,最大线程数=Integer.MAX_VALUE,一直存活,使用优先级队列DelayedWorkQueue定时,默认线程工厂Executors.defaultThreadFactory();默认阻塞策略:AbortPolicy

 DelayedWorkQueue优先级队列,底层使用“堆”数据结构实现

ExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

SpringBoot线程池

SpringBoot线程池在JUC包下ThreadPoolExecutor基础上进行封装,通过注入Bean或注解的方式使用。SpringBoot提供的线程池包括普通提交task的线程池和定时线程池。

提交task方式

通过@Configuration注解和@Bean注解,注入定义的ThreadPoolTaskExecutor对象,通过@Autowired注入使用的threadPoolTaskExecutor对象,通过threadPoolTaskExecutor的submit()或execute()方法提交任务。

注意:@Autowired注入@Bean注解对象时,默认是@Bean对象的方法名

@Configuration
public class TaskThreadPoolConfig {
//将线程池注入Spring IOC
	@Bean
	public ThreadPoolTaskExecutor threadPoolTimeoutExecutor() {
		ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
		threadPoolTaskExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors() * 2);
		threadPoolTaskExecutor.initialize();
		return threadPoolTaskExecutor;
	}
	
//注入线程池对象
	@Autowired
	ThreadPoolTaskExecutor threadPoolTimeoutExecutor;

//提交任务
public void test(){
		threadPoolTimeoutExecutor.submit(()->{
			runTask();
		});
	}
}

注解方式

通过@Configuration注解和@Bean注解,注入定义的ThreadPoolTaskExecutor对象,通过@Async注解异步执行方法,方法可以返回task的Future句柄,或返回void

注意:@Bean注解可以添加线程池名字,异步方法@Async可以指定线程池名字

@Configuration
public class TaskThreadPoolConfig {
	@Bean(value = "test-threadpool")//定义线程池名
	public ThreadPoolTaskExecutor threadPoolTimeoutExecutor() {
		ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
		threadPoolTaskExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors() * 2);
		threadPoolTaskExecutor.initialize();
		return threadPoolTaskExecutor;
	}

//方法上添加注解,该方法异步执行,方法可以返回Future任务句柄,或void
	@Async(value = "test-threadpool")//指定线程池
	public Future<Object> runTask(){
		System.out.println("--");
		return new AsyncResult<>(true);
	}
}

取消任务

获取task的Future句柄,通过future.cancel(true)取消任务。其内部则通过翻转interrupted标志位,遇到例如sleep、wait、join等则抛出中断异常,终止执行task的线程。

	@Autowired
	ThreadPoolTaskExecutor threadPoolTimeoutExecutor;
	public static void main(String []args){
		Future<?> future = threadPoolTimeoutExecutor.submit(()->{
			try {
				Thread.sleep(1000l);
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
		});
		if(!future.isDone()){//task未结束
			future.cancel(true);//翻转执行task的线程的中断标志位,遇到例如sleep,join,wait等抛出中断异常终止task
		}
	}

觉得不错,点个👍吧,😄

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

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

相关文章

探索人工智能领域——每日20个名词详解【day8】

目录 前言 正文 总结 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo__✍️原创&#xff0c;首发于CSDN&#x1f4da;。 &#x1f4e3;如需转载&#xff0c;请事先与我联系以…

使用UART和USART在STM32上进行双向通信

在本文中&#xff0c;我们将深入了解如何在STM32上使用UART&#xff08;通用异步收发传输器&#xff09;和USART&#xff08;通用同步异步收发传输器&#xff09;实现双向通信。UART和USART是常见的串口通信协议&#xff0c;通常用于与其他设备进行数据传输。我们将重点介绍如何…

01_W5500简介

目录 W5500简介&#xff1a; 芯片特点: 全硬件TCPIP协议栈: 引脚分布&#xff1a; W5500简介&#xff1a; W5500是一款高性价比的以太网芯片&#xff0c;其全球独一无二的全硬件TCPIP协议栈专利技术&#xff0c;解决了嵌入式以太网的接入问题&#xff0c;简单易用&#xff…

redis 安装在liunx安装和常用文件配置

文章目录 安装配置文件设置测试启动服务连接服务 安装 1.官网下载压缩包: https://redis.io/download/ 2.将压缩包上传到Linux环境中 解压: tar -xvf redis-xxxxx 3.liunx 需要c的环境 yum -y install gcc-c4.进入redis文件夹 make && make install5.推荐不是必须…

CPP-SCNUOJ-Problem P24. [算法课贪心] 跳跃游戏

Problem P24. [算法课贪心] 跳跃游戏 给定一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。 数组中的每个元素代表你在该位置可以跳跃的最大长度 判断你是否能够到达最后一个下标。 输入 输入一行数组nums 输出 输出true/fasle 样例 标准输入 2 3 1 …

spring cloud 整合Feign经行远程调用

文章目录 Feign远程调用Feign替代RestTemplate1&#xff09;引入依赖2&#xff09;添加注解3&#xff09;编写Feign的客户端4&#xff09;测试5&#xff09;总结 自定义配置配置文件方式Java代码方式 Feign使用优化 Feign远程调用 先来看我们以前利用RestTemplate发起远程调用…

GORM 多对多many2many 自定义连接表

文章目录 多对多 many2many表结构搭建多对多添加多对多查询多对多的删除、更新 自定义连接表生成表结构操作案例添加文章并添加标签&#xff0c;并自动关联添加文章&#xff0c;关联已有标签给已有文章关联标签替换已有文章的标签查询文章列表&#xff0c;显示标签 自定义连接…

陀螺仪LSM6DSV16X与AI集成(2)----姿态解算

陀螺仪LSM6DSV16X与AI集成.2--姿态解算 概述视频教学样品申请完整代码下载欧拉角万向节死锁四元数法姿态解算双环PI控制器偏航角陀螺仪解析代码上位机通讯加速度演示陀螺仪工作方式主程序演示 概述 LSM6DSV16X包含三轴陀螺仪与三轴加速度计。 姿态有多种数学表示方式&#xff…

TCL - 库编译过程和官方手册

文章目录 TCL - 库编译过程和官方手册概述笔记编译步骤TCL官方手册END TCL - 库编译过程和官方手册 概述 想看看sqlite3的官方demo工程, 没看到. 想编译一下sqlite3源码, 看看编译后有没有example 工程. 看了sqlite3的官方说明, 他们工程使用tcl来编译的. 一听tcl, 咋这么耳熟…

Ribbon 负载均衡

1、负载均衡整体流程 2、负载均衡流程逐级跟踪运行 (1) LoadBlanced 注解可以使LoadBalancerInterceptor拦截到&#xff1b; (2)LoadBalancerInterceptor 实现了ClientHttpRequestInterceptor接口&#xff1b; (3)ClientHttpRequestInterceptor接口释义如下&#xff1b; (4)int…

k8s引用环境变量

一 定义环境变量 ① 如何在k8s中定义环境变量 env、configmap、secret补充&#xff1a; k8s 创建Service自带的环境变量 ② 从pod属性中获取 kubectl explain deploy.spec.template.spec.containers.env.valueFrom关注&#xff1a; configMapKeyRef、fieldRef 和 resour…

zxjy003- Spring Cloud后端工程搭建

一、创建父工程 1、创建 sprigboot 工程 guli-parent groupId &#xff1a; com.atguigu artifactId &#xff1a; guli-parent

RK3568平台开发系列讲解(Linux系统篇)device_node 转换成 platform_device

🚀返回专栏总目录 文章目录 一、DTB转换规则二、转换源码分析沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍通过设备树 device_node 转换成 platform_device 一、DTB转换规则 device 部分是用 platform_device 结构体来描述硬件资源的, 所以内核最终会将…

【设计模式-3.1】结构型——外观模式

说明&#xff1a;本文介绍设计模式中结构型设计模式中的&#xff0c;外观模式&#xff1b; 亲手下厨还是点外卖&#xff1f; 外观模式属于结构型的设计模式&#xff0c;关注类或对象的组合&#xff0c;所呈现出来的结构。以吃饭为例&#xff0c;在介绍外观模式之前&#xff0…

蓝桥杯网络安全组竞赛

竞赛规则及说明 选拔赛时长&#xff1a;4h 决赛时长&#xff1a;4h 竞赛形式&#xff1a;线上比赛&#xff1a; 个人赛&#xff1a;一人一机&#xff0c;全程机考 大赛制定竞赛系统&#xff0c;在时间内提交答案到比赛系统&#xff0c;超时无法提交 机器环境&#xff1a; 电脑…

matplotlib多子图

matplotlib画图中一个轴占据多个子图 - 知乎 import matplotlib.pyplot as plt fig plt.figure() gs fig.add_gridspec(2,4) ax1 fig.add_subplot(gs[0, 0:2]) ax2 fig.add_subplot(gs[0, 2:]) axa fig.add_subplot(gs[1, 1]) axb fig.add_subplot(gs[1, 2]) axc fig.add…

编写Java应用程序,输出满足1+2+3+……+n<8888的最大正整数n。

源代码&#xff1a; public class Main { public static void main(String[] args) { int i 1; int sum 0; for(i 1;;i){ sum i; if (sum >8888) break; } System.out.println(i-1); } } 实验运行截图&#xff1a;

隐写术和人工智能

在一项新的研究中&#xff0c;人工智能对齐研究实验室 Redwood Research 揭示了大型语言模型 (LLM) 可以掌握“编码推理”&#xff0c;这是一种隐写术形式。 这种有趣的现象使得大型语言模型能够以人类读者无法理解的方式巧妙地将中间推理步骤嵌入到生成的文本中。 大型语言…

【滤波第二期】中值滤波的原理和C代码

中值滤波是一种非线性数字滤波技术&#xff0c;主要应用于信号处理和图像处理领域&#xff0c;用于减小信号中的噪声和离群值。中值滤波的核心思想是通过计算一组数据点的中间值&#xff0c;以抑制脉冲噪声等离群值的影响&#xff0c;从而实现信号的平滑处理。 1&#xff0c;中…

SaToken利用Redis做持久化

官网解释 官网解释 教程 引入依赖 <!-- 提供Redis连接池 --> <dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId> </dependency><!-- Sa-Token 整合 Redis &#xff08;使用 jdk 默认序…
最新文章