线程池、及Springboot线程池实践

news2024/9/28 21:29:49

摘要

本文介绍了线程池基本概念、线程及线程池状态、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.coloradmin.cn/o/1284016.html

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

相关文章

HCIP——交换综合实验

一、实验拓扑图 二、实验需求 1、PC1和PC3所在接口为access&#xff0c;属于vlan2&#xff1b;PC2/4/5/6处于同一网段&#xff0c;其中PC2可以访问PC4/5/6&#xff1b;但PC4可以访问PC5&#xff0c;不能访问PC6 2、PC5不能访问PC6 3、PC1/3与PC2/4/5/6/不在同一网段 4、所有PC通…

【Java】类和对象之超级详细的总结!!!

文章目录 前言1. 什么是面向对象&#xff1f;1.2面向过程和面向对象 2.类的定义和使用2.1什么是类&#xff1f;2.2类的定义格式2.3类的实例化2.3.1什么是实例化2.3.2类和对象的说明 3.this引用3.1为什么会有this3.2this的含义与性质3.3this的特性 4.构造方法4.1构造方法的概念4…

数据结构第六课 -----链式二叉树的实现

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

Java生成word[doc格式转docx]

引入依赖 <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker --><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.32</version></dependency> doc…

针对net core 使用CSRedis 操作redis的三种连接实例方式

1、主从访问 2、哨兵模式 3、集群访问 写法一&#xff1a;写任意一个地址即可&#xff0c;其它节点在运行过程中自动增加&#xff0c;确保每个节点密码一致。如&#xff1a;Console.WriteLine("集群测试");RedisHelper.Initialization(new CSRedis.CSRedisClient(&q…

Http和WebSocket

客户端发送一次http请求&#xff0c;服务器返回一次http响应。 问题&#xff1a;如何在客户端没有发送请求的情况下&#xff0c;返回服务端的响应&#xff0c;网页可以得服务器数据&#xff1f; 1&#xff1a;http定时轮询 客户端定时发送http请求&#xff0c;eg&#…

回溯和分支算法

状态空间图 “图”——状态空间图 例子&#xff1a;农夫过河问题——“图”状态操作例子&#xff1a;n后问题、0-1背包问题、货郎问题(TSP) 用向量表示解&#xff0c;“图”由解向量扩张得到的解空间树。 ——三种图&#xff1a;n叉树、子集树、排序树 ​ 剪枝 不满住条件的…

链表【3】

文章目录 &#x1f433;23. 合并 K 个升序链表&#x1f41f;题目&#x1f42c;算法原理&#x1f420;代码实现 &#x1f437;25. K 个一组翻转链表&#x1f416;题目&#x1f43d;算法原理&#x1f367;代码实现 &#x1f433;23. 合并 K 个升序链表 &#x1f41f;题目 题目链…

Sentinel基础知识

Sentinel基础知识 资源 1、官方网址&#xff1a;https://sentinelguard.io/zh-cn/ 2、os-china: https://www.oschina.net/p/sentinel?hmsraladdin1e1 3、github: https://github.com/alibaba/Sentinel 一、软件简介 Sentinel 是面向分布式服务架构的高可用流量防护组件…

Unity 关于SetParent方法的使用情况

在设置子物体的父物体时&#xff0c;我们使用SetParent再常见不过了。 但是通常我们只是使用其中一个语法&#xff1a; public void SetParent(Transform parent);使用改方法子对象会保持原来位置&#xff0c;跟使用以下方法效果一样&#xff1a; public Transform tran; ga…

【数值计算方法(黄明游)】函数插值与曲线拟合(二):Newton插值【理论到程序】

​ 文章目录 一、近似表达方式1. 插值&#xff08;Interpolation&#xff09;2. 拟合&#xff08;Fitting&#xff09;3. 投影&#xff08;Projection&#xff09; 二、Lagrange插值1. 拉格朗日插值方法2. Lagrange插值公式a. 线性插值&#xff08;n1&#xff09;b. 抛物插值&…

UDS 诊断报文格式

文章目录 网络层目的N_PDU 格式诊断报文的分类&#xff1a;单帧、多帧 网络层目的 N_PDU(network protocol data unit)&#xff0c;即网络层协议数据单元 网络层最重要的目的就是把数据转换成符合标准的单一数据帧&#xff08;符合can总线规范的&#xff09;&#xff0c;从而…

原生横向滚动条 吸附 页面底部

效果图 /** 横向滚动条 吸附 页面底部 */ export class StickyHorizontalScrollBar {constructor(options {}) {const { el, style } optionsthis.createScrollbar(style)this.insertScrollbar(el)this.setScrollbarSize()this.onEvent()}/** 创建滚轴组件元素 */createS…

【踩坑】解决maven的编译报错Cannot connect to the Maven process. Try again later

背景 新公司新项目, 同事拷给我maven的setting配置文件, 跑项目编译发现maven报 Cannot connect to the Maven process. Try again later. If the problem persists, check the Maven Importing JDK settings and restart IntelliJ IDEA 虽然好像不影响, 项目最终还是能跑起来…

计算机组成学习-存储系统总结

复习本章时&#xff0c;思考以下问题&#xff1a; 1)存储器的层次结构主要体现在何处&#xff1f;为何要分这些层次&#xff1f;计算机如何管理这些层次&#xff1f;2)存取周期和存取时间有何区别&#xff1f;3)在虚拟存储器中&#xff0c;页面是设置得大一些好还是设置得小一…

视频剪辑转码:mp4批量转成wmv视频,高效转换格式

在视频编辑和处理的领域&#xff0c;转换格式是一项常见的任务。在某些编辑和发布工作中&#xff0c;可能需要使用WMV格式。提前将素材转换为WMV可以节省在编辑过程中的时间和精力。从MP4到WMV的批量转换&#xff0c;不仅能使视频素材在不同的平台和设备上得到更好的兼容性&…

JavaScript基础—for语句、循环嵌套、数组、操作数组、综合案例—根据数据生成柱形图、冒泡排序

版本说明 当前版本号[20231129]。 版本修改说明20231126初版20231129完善部分内容 目录 文章目录 版本说明目录JavaScript 基础第三天笔记for 语句for语句的基本使用循环嵌套倒三角九九乘法表 数组数组是什么&#xff1f;数组的基本使用定义数组和数组单元访问数组和数组索引…

centos7 设置静态ip

文章目录 设置VMware主机设置centos7 设置 设置VMware 主机设置 centos7 设置 vim /etc/sysconfig/network-scripts/ifcfg-ens33重启网络服务 service network restart检验配置是否成功 ifconfig ip addr

爬虫概念、基本使用及一个类型和六个方法(一)

目录 一、爬虫简介 1.什么是爬虫 2.爬虫的核心 3.爬虫的用途 4.爬虫的分类 5.反爬手段 二、Urllib基本使用 1.导入我们需要的包 2.定义一个url 3.模拟浏览器向服务器发送请求 4.获取响应中的页面的源码 5.打印数据 三、一个类型和六个方法 1.定义url&#xff0c;并…

BFS求树的宽度——结合数组建树思想算距离

二叉树最大宽度 https://leetcode.cn/problems/maximum-width-of-binary-tree/description/ 1、考虑树的宽度一定是在一层上的所以进行BFS&#xff0c;树的BFS不建议直接使用队列&#xff0c;每次add/offer然后poll/remove&#xff0c;这样子层级关系不好显示。我们可以定义…