线程池的基本创建方式and执行流程!

2023-11-05

 一、什么是线程池?

1.线程池

线程池是一种线程管理的机制,它是一组线程的集合,可以用来执行多个任务。线程池维护了一个固定数量的线程集合,可以从线程池中取出一个线程来执行任务,当任务执行完毕后,线程又会返回到线程池中,等待下一个任务的到来。当所有线程都处于忙碌状态时,线程池会创建一个新的线程进行处理,如果超过线程最大数,则进入等待队列,进行等待。

2.线程池的优点 

  • 资源控制:线程池可以限制线程数量,避免线程过多导致系统资源不足。

  • 提高响应速度:线程池中的线程已经预先创建,可以避免线程创建时的开销,从而可以更快地响应请求。

  • 提高系统稳定性:线程池可以避免系统过载,从而提高系统的稳定性和可靠性。

  • 提高资源利用率:线程池中的线程可以重复利用,避免了线程的频繁创建和销毁,从而提高了资源的利用率。

二、线程池的创建方式

  • Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
public class Test01 {
	public static void main(String[] args) throws InterruptedException {
		//创建固定数目的线程池
		ExecutorService executorService=Executors.newFixedThreadPool(4);
		
		//提交六个线程任务
		for(int i=0;i<6;i++) {
			executorService.execute(new Task("线程"+i));
		}
		executorService.shutdown();
		
		while(!executorService.awaitTermination(1, TimeUnit.SECONDS)) {
			System.out.println("线程池暂未关闭!");
		}
		System.out.println("线程池已经关闭!");
	}

}
  • Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;
public class Test02 {
	public static void main(String[] args) throws InterruptedException {
	
	ExecutorService executorService=Executors.newCachedThreadPool();
	
	//提交六个线程任务
	for(int i=0;i<60;i++) {
		executorService.execute(new Task("线程"+i));
	}
	executorService.shutdown();
	
	while(!executorService.awaitTermination(1, TimeUnit.SECONDS)) {
		System.out.println("线程池暂未关闭!");
	}
	System.out.println("线程池已经关闭!");
}

}
  • Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;
  • Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;
//周期性执行线程任务
public class Test04 {
	public static void main(String[] args) {
		ScheduledExecutorService executorService=Executors.newScheduledThreadPool(3);
		
		//延迟3秒钟执行任务执行一次
//		System.out.println("当前时间:"+LocalDateTime.now());
//		executorService.schedule(new Task("任务A"), 3, TimeUnit.SECONDS);
		
		//延迟一秒钟后执行任务,每隔3秒钟执行一次
//		System.out.println("当前时间:"+LocalDateTime.now());
//		executorService.scheduleAtFixedRate(new Task("任务A"), 1, 3, TimeUnit.SECONDS);
		
		System.out.println("当前时间:"+LocalDateTime.now());
		executorService.scheduleWithFixedDelay(new Task("任务A"), 1, 3, TimeUnit.SECONDS);
		
	}

}
  • ThreadPoolExecutor:最原始的创建线程池的方式,需要设置参数。
  1. 第一个参数corePoolSize:是指核心线程数,线程池中始终存活的线程数。
  2. 第二个参数maximumPoolSize:最大线程数,线程池中允许的最大线程数,当线程池的任务队列满了之后可以创建的最大线程数。
  3. 第三个参数keepAliveTime:非核心线程空闲后可以存活的时间,当线程中没有任务执行时,最大线程就会销毁一部分,最终保持核心线程数量的线程。
  4. 等四个参数为单位:是和参数 三存活时间配合使用的,合在一起用于设定线程的存活时间 ,参数 keepAliveTime 的时间单位有以下 7 种可选。
  5. 第五个参数workQueue:一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全。
  6. 第六个参数threadFactory:线程工厂,主要用来创建线程,默认为正常优先级、非守护线程。

常见的阻塞队列 

  1. ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
  2. LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
  3. SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
  4. PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
  5. DelayedWorkQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素。
public class Test05 {
	public static void main(String[] args) {
		ExecutorService executorService=new ThreadPoolExecutor(10, 11, 1, TimeUnit.SECONDS, 
				new LinkedBlockingDeque<Runnable>(),
				new MyThreadFactory());	
		
		//提交六个线程任务
		for(int i=0;i<60;i++) {
			executorService.execute(new Task("线程"+i));
		}
				
	}

}
public class MyThreadFactory implements ThreadFactory {
	
	private final AtomicInteger number=new AtomicInteger();

	@Override
	public Thread newThread(Runnable r) {
		Thread t=new Thread(r);
		t.setName("订单线程"+number.incrementAndGet());
		return t;
	}

}
class Task implements Runnable{
	private String TaskName;
	
	public Task(String taskName) {
		this.TaskName = taskName;
	}

	@Override
	public void run() {
		System.out.println("启动线程===>"+this.TaskName);
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("结束线程===>"+this.TaskName);
		
	}	
}

三、线程的常用接口/类和方法

1.接口/类: 

ExecutorService接口:进行线程池的操作访问。

Executors类:创建线程池的工具类。

ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制。

 2.执行线程任务

awaitTermination(long timeout,TimeUnit unit) :每间隔一定时间检查一次线程执行任务状态,等待线程池关闭。

public class Test01 {
	public static void main(String[] args) {
		//创建1-100w之间的多有数字累加和,每10W个数字交给一个线程处理
		//创建一个固定大小的线程池
		ExecutorService executorService=Executors.newFixedThreadPool(4);
		
		//创建集合用于保存Future执行结果
		List<Future<Integer>> futureList=new ArrayList<Future<Integer>>();
		
		//每10w个数字封装成一个callable线性任务,并提交给线程池
		for(int i=0;i<900000;i+=100000) {
			Future<Integer> total=executorService.submit(new CalcTask(i, i+100000));
			futureList.add(total);		
		}
		
		try {
			int total=0;
			for(int i=0;i<futureList.size();i++) {
				total+=futureList.get(i).get();
			}
			System.out.println("最终计算结果:"+total);
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		executorService.shutdown();
		
		//没间隔一秒钟检查一次线程池的任务执行状态
		try {
			while(executorService.awaitTermination(1, TimeUnit.SECONDS));
			System.out.println("还没有关闭");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("线程关闭了!");
		
	}
	
	

}


class CalcTask implements Callable<Integer>{
	private int begin,end;
	
	public CalcTask(int begin,int end) {
		this.begin=begin;
		this.end=end;
	}

	@Override
	public Integer call() throws Exception {
		int total=0;
		for(int i=begin;i<=end;i++) {
			total+=i;
		}
		System.out.printf("线程%s计算%d-%d范围的任务结束!\n",Thread.currentThread().getName(),begin,end);
		return total;
	}
	
}

线程的关闭

 shutdown():关闭线程池,但会等正在执行任务的线程结束再关闭。

shutdownNow():立即关闭。

四、线程池的执行流程 

线程池中的线程数量是有限的,每个线程可以处理多个任务。线程池的工作原理主要分为以下几个步骤:

  1. 创建一个线程池:初始化线程池,为线程池分配一定数量的线程,设置线程池中线程的最大数量、最小数量、空闲线程的存活时间等属性。
  2. 将任务加入线程池:当有任务需要处理时,线程池会从任务队列中取出一个任务,将其分配给一个空闲线程进行处理,如果没有空闲线程那么线程池会对判断当前存活的线程数是否小于核心线程数。如果小于,则创建一个新的线程去处理新任务;如果大于核心线程数则回去检查工作等待队列,如果工作队列未满则会放入工作队列中进行等到有空闲线程去处理它,如果工作队列已经满了,则需要判断当前存活的线程数是否达到最大线程数,如果没有就创建一个新的非核心线程进行处理新任务,如果达到了,则采用拒绝策略处理新任务。
  3. 线程处理任务:线程取出任务后,进行任务处理,处理完毕后继续从任务队列中取出任务进行处理。
  4. 任务队列:线程池中的任务队列用于存放等待执行的任务,线程池中的线程会从任务队列中取出任务进行处理。
  5. 线程池关闭:当不需要处理任务时,可以关闭线程池,线程池会将所有未执行的任务清空,并释放所有的线程资源。

拒绝策略 

常见的线程池拒绝策略有:

  1. CallerRunsPolicy:由调用线程处理该任务,即在提交任务的线程中执行该任务,谁调用谁处理。

  2. AbortPolicy:直接抛出异常,阻止系统正常工作。

  3. DiscardPolicy:直接丢弃任务,不进行处理。

  4. DiscardOldestPolicy:丢弃队列中最旧的任务,尝试为当前任务腾出位置。

 五、线程池的状态

线程池的状态分为:running, shutdown,stop,tidying,terminated。

running

  • 状态说明:线程池处在运行状态时,能够接收新任务,并处理工作队列中的任务。 
  • 状态切换:线程池的初始化状态是running。换句话说,线程池被一旦被创建,就处于running状态,并且线程池中的任务数为0。

shutdown

  • 状态说明:线程池处在关闭状态时,不接收新任务,但能处理已添加的任务。 
  • 状态切换:调用线程池的shutdown()接口时,线程池由running-->shutdown。

stop

  • 状态说明:线程池处在停止状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。 
  • 状态切换:调用线程池的shutdownNow()接口时,线程池由running -->stop。

tidying

  • 状态说明:整理状态。该状态表明所有的任务已经运行终止,记录的任务数量为0;线程池中执行的任务为空,进入tidying状态。
  • 状态切换:当线程池在shutdown状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 shutdown-->tidying。 当线程池在stop状态下,线程池中执行的任务为空时,就会由stop-->tidying。

terminated

  •  状态说明:线程池彻底终止,就变成terminated状态。 
  • 状态切换:线程池处在tidying状态时,执行完terminated()之后,就会由tidying-->terminated。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程池的基本创建方式and执行流程! 的相关文章

  • 将 GAE/J 上的 pdfjet 生成的 PDF 文件上传到 Google Docs

    我需要将 PDF 文件上传到用户谷歌文档 该文件是由谷歌应用引擎上的 pdfjet 生成的 我想出使用 pdfjet for gae j 生成 pdf pdfjet 使用流来创建 pdf 无论如何 是否可以将流转换为文件 以便我可以上传给用
  • setSize() 不起作用?

    我有一个程序 需要两个按钮 一个是常规按钮 另一个具有根据鼠标悬停而变化的图片 目前 由于图片很大 JButton自定义也很大 我可以更改自定义的大小并保持图像 和翻转图像 成比例吗 我尝试过 setSize 但它没有任何作用 对于任何反馈
  • Java中单击和双击的区别

    我搜索论坛并看到以下代码 public void mouseClicked MouseEvent e if e getClickCount 2 System out println and it s a double click wasDo
  • Tomcat:Java 静态变量作用域、应用程序范围还是会话范围?

    java 静态变量是否在使用相同 web 应用程序的所有会话之间共享 或者每个会话都有自己的静态变量版本吗 换句话说 Tomcat 是为每个会话创建一组新的类 还是为整个 Web 应用程序创建一组新的类 Tomcat 创建一个ClassLo
  • JP QL - 一对多关系中的过滤结果

    我在尝试构建 JPQL 查询时陷入困境 并希望比我拥有更多 JPA 经验的人能够提供帮助 考虑以下两个实体 class Author String name OneToMany mappedBy author Set
  • 给定一个 IP 地址列表,如何找到最小值、最大值?

    在Java中 我有一个ip地址的数组列表 我如何找到最小值和最大值 我已经使用了 Collection min 但在以下情况下它不起作用 192 168 0 1 lt min 192 168 0 250 192 168 0 9 lt max
  • Struts ActionForm 属性应该是什么类型?

    我使用 Struts 1 2 4 继承了这个巨大的遗留 Java Web 应用程序 我有一个关于 ActionForms 的具体问题 其中一些仅具有字符串属性 即使对于数字 其中一些使用看似合适的类型 整数 日期 字符串等 这里的最佳实践是
  • 由于 maven-surefire-plugin,Maven 构建失败

    我这里有类似的问题eclipse 中缺少 maven surefire plugin https stackoverflow com questions 23588957 maven surefire plugin missing in e
  • Spring Security 的 AJAX 请求给出 403 Forbidden

    我有一个基于spring boot spring security thymeleaf的网站 在某些情况下我也使用ajax 问题 我在 Spring Security 中使用表单登录安全性 在浏览器中 登录后我可以使用rest API GE
  • 如何将 java ArrayList 转换为等效的 double[] [重复]

    这个问题在这里已经有答案了 可能的重复 如何在 Java 中从 List 转换为 double https stackoverflow com questions 6018267 how to cast from listdouble to
  • 将 Flash 文件上传与 JSF 集成

    我看到我们可以通过flash文件上传来上传多个文件 喜欢SWF上传 http code google com p swfupload or YUI上传器 http yuilibrary com yui docs uploader 是否可以将
  • 动态更新 LookAndFeel 值

    我希望能够动态更新 Swing GUI 的 LookAndFeel 属性 在本例中 我有一个简单的 Swing Awt 游戏 运行最初为 Nimbus 的游戏LookAndFeel 在启动后的各个时刻 我只想更改 比方说 一个细节 应用程序
  • 在 Spring Boot 异常处理期间保留自定义 MDC 属性

    简短版本 有足够的细节 如何保留添加在MDC中的属性doFilter 的方法javax servlet Filter执行 public void doFilter ServletRequest request ServletResponse
  • Spring Boot 1.4:Liquibase完成后的执行方法

    我有一个基于 Spring Boot 1 4 0 的项目 该项目使用 Liquibase liquibase 完成后是否可以执行方法 像 Bean 后处理器之类的东西 我想要做的是当应用程序在开发模式下启动时向我的数据库添加一些数据 在开发
  • 如何在 jax-ws 客户端中隐藏(可能)由 jax-ws 库引起的警告

    我正在使用 netbeans 在我的应用程序中生成 Web 服务客户端 我的程序使用 jax ws 库来设置调用 Web 服务的超时 出现问题是因为每当我启动这个程序时它都会生成很多这样的警告消息 2010 年 12 月 13 日下午 4
  • 无法实例化类对象的类型 (Java)

    这是我收到错误的代码 在 new 之后的第二个 Killer 处 String classes new String 5 kills 0 Brian Moser kills 1 James Doakes kills 2 Lila Tourn
  • 如何使用 log4j2.xml 配置 hibernate 日志记录?

    我最近切换到 Apache log4j2 但仍然找不到使用 log4j2 xml 配置 hibernate 日志记录的方法 因为我找不到解决此问题的方法 所以我仍然显式使用 log4j properties 文件进行休眠 这不是最好的解决方
  • Spring Boot 和安全性以及自定义 AngularJS 登录页面

    我正在为 Spring Security 实现一个自定义 AngularJS 登录页面 但遇到身份验证问题 遵循本教程 示例 以及他们的示例在本地运行良好 https github com dsyer spring security ang
  • Java 和 SQL Server 中的精度噩梦

    我一直在与 Java 和 SQL Server 中的精确噩梦作斗争 直到我不再知道了 就我个人而言 我理解这个问题及其根本原因 但向地球另一端的客户解释这一点是不可行的 至少对我来说 情况是这样的 我在 SQL Server 中有两列 Qt
  • Spring Data JPA 存储库,具有规范、分页和标准 fetch-join

    我正在使用具有规范和分页功能的 Spring Data JPA 存储库实现实体列表的搜索 过滤服务 我正在尝试减少查询数量 n 1 问题 并使用条件获取机制获取嵌套数据 我有两个实体类 Entity Table name delegatio

随机推荐

  • XXL-JOB(分布式任务调度平台)的使用(详细教程)

    概述 首先我们要知道什么是XXL JOB 官方简介 XXL JOB是一个分布式任务调度平台 其核心设计目标是开发迅速 学习简单 轻量级 易扩展 现已开放源代码并接入多家公司线上产品线 开箱即用 XXL JOB的有点特性 1 简单 支持通过W
  • Hibernate 项目查询数据报 UnknownEntityTypeException

    原因分析 1 hibernate cfg xml配置文件有没有映射实体类
  • 一文带你全面理解向量数据库

    近些年来 向量数据库引起业界的广泛关注 一个相关事实是许多向量数据库初创公司在短期内就筹集到数百万美元的资金 你很可能已经听说过向量数据库 但也许直到现在才真正关心向量数据库 至少 我想这就是你现在阅读本文的原因 如果你阅读本文只是为了简单
  • wireshark过滤器的使用

    目录 wireshark wireshark的基本使用 wireshark过滤器的区别 抓包案例 wireshark wireshark的基本使用 抓包采用 wireshark 提取特征时 要对 session 进行过滤 找到关键的stre
  • 华为云使用手册

    华为云重磅福利 云主机 海外云主机 云容器和多款云产品0元领取 华为云重磅推出云上优选 特惠来袭来迎接这个来之不易的春天 本次活动依然是给到了很低的折扣 0 7折起 活动走起 福利1 免费试用海外云主机和云原生容器网页连接 进入免费试用专区
  • CentOS安装python3.x最新版和chrome chromedriver

    之前使用selenium wire的响应拦截器获取请求头中的签名需要部署到服务器 所以得搭建一个服务器运行环境 安装过程有坑 这里记录一下 Linux平台安装需要下载源码包自己编译 下载地址 https www python org dow
  • hexo主题标签的使用

    https akilar top posts 615e2dec 这个是我看的教程 我直接复制的源码 友情链接 LrcShare 实现hexo标签的可以折叠 hexo标签的使用方法 要实现Hexo标签的可折叠 可以使用Hexo内置的foldi
  • ad中按钮开关的符号_收藏:电路图符号大全

    电子设备中有各种各样的图 能够说明它们工作原理的是电原理图 简称电路图 电路图是说明模拟电子电路工作原理的 它用各种图形符号表示电阻器 电容器 开关 晶体管等实物 用线条把元器件和单元电路按工作原理的关系连接起来 一张电路图就好像是一篇文章
  • 在SpringBoot中加入jsp

    SpringBoot官方不推荐在 SpringBoot 中使用 jsp 的 那么到底可以使用吗 答案是肯定的 不过需要导入tomcat 插件启动项目 不能再用 SpringBoot 默认 tomcat 了 一 导入SpringBoot的to
  • React实现大文件上传、react-dropzone

    React大文件上传的实现方案大致如下 使用第三方组件库实现文件上传 如react dropzone 将大文件分成多个小块 并使用XMLHttpRequest或者fetch发送分块上传请求 为了保证数据完整性 每个请求都需要携带校验码 在上
  • (0)JavaScript语法---小程序回调函数【幼儿园级教程】

    微信小程序中的回调函数 史上最简单的幼儿园基础教程 小程序的回调函数 汉字版的编码 你是不是第一次见到 总结 小程序的回调函数 在小程序包含逻辑时 回调函数几乎是无法避免 在整个使用中 发现大部分帖子都是针对有一定的基础的伙伴写的 也比较晦
  • Flink 1.11:更好用的流批一体 SQL 引擎

    许多的数据科学家 分析师和 BI 用户依赖交互式 SQL 查询分析数据 Flink SQL 是 Flink 的核心模块之一 作为一个分布式的 SQL 查询引擎 Flink SQL 提供了各种异构数据源的联合查询 开发者可以很方便地在一个程序
  • 树莓派Tools交叉编译OpenGL(mesa-12.0.5)

    以下shell命令都是在root权限下执行的 得按照顺序来 不然会报找不到包或者一些文件找不到这些话 交叉编译玩多了 自己翻来覆去 整多了后其实也就融会贯通了 有耐心就行 宗旨就是你得让它们找到的到对应文件 一般要么在环境变量里去pkg c
  • 【牛客SQL】SQL19 查找所有员工的last_name和first_name以及对应的dept_name

    题目描述 描述 有一个员工表employees简况如下 有一个部门表departments表简况如下 有一个 部门员工关系表dept emp简况如下 请你查找所有员工的last name和first name以及对应的dept name 也
  • Git如何删除本地仓库

    删除仓库 就是需要删除仓库文件夹下隐藏的 git 文件夹 进入项目所在目录 打开git bash 开始删除本地仓库 显示所有本地分支 初始化时只有一个master分支 git branch 初始化本地版本库 重新初始化一次 可以忽略 git
  • 数据结构——队列

    创建队列 塞值和拿值 当我们创建一个LinkedList的时候 就可以用来模拟队列 因为该集合里有大量操作首尾元素的方法 之后就可以在该队列里进行数据的添加和获取 但是当我们使用数组来实现时 如何创建一个队列呢 最大值怎么确定 首尾初始值怎
  • 新闻主题识别及其热点演化分析流程

    1 数据收集 收集与科技新闻相关的大量文本数据 包括新闻报道 评论 社交媒体等 2 数据预处理 对收集到的文本数据进行清洗 去重 分词 停用词过滤等处理 3 特征提取 采用TF IDF Word2Vec等技术进行文本特征提取 将文本转化为向
  • Android JNI打印logcat日志

    在 JNI 中打印日志可以使用 android log print 函数来实现 该函数是 Android NDK 提供的一个用于在本地代码中输出日志消息到 logcat 的方法 要在 JNI 中打印日志 请按照以下步骤进行操作 在你的 JN
  • verilog除法器设计

    除法器原理 和十进制除法类似 计算 27 除以 5 的过程如下所示 除法运算过程如下 1 取被除数的高几位数据 位宽和除数相同 实例中是 3bit 数据 2 将被除数高位数据与除数作比较 如果前者不小于后者 则可得到对应位的商为 1 两者做
  • 线程池的基本创建方式and执行流程!

    一 什么是线程池 1 线程池 线程池是一种线程管理的机制 它是一组线程的集合 可以用来执行多个任务 线程池维护了一个固定数量的线程集合 可以从线程池中取出一个线程来执行任务 当任务执行完毕后 线程又会返回到线程池中 等待下一个任务的到来 当