Java并发编程(六)线程池[Executor体系]

概述

在处理大量任务时,重复利用线程可以提高程序执行效率,因此线程池应运而生。

  • 它是一种重用线程的机制,可以有效降低内存资源消耗
  • 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行
  • 线程池可以帮助我们更好地管理线程的生命周期和资源使用,避免线程频繁地创建和销毁带来的性能问题

同时,线程池还可以提供一些额外的功能,例如线程池的大小控制、线程池的任务队列、线程池的拒绝策略等。线程池中通常维护一个线程队列,线程队列中保存着已创建的线程,当有新的任务需要执行时,线程池中的线程就可以从队列中取出一个线程来执行任务,任务执行完毕后线程可以被放回线程队列中,等待下一个任务的到来

核心执行流程

线程池执行流程
  • 当有新任务需要线程执行时,线程池会先判断是否有空闲的核心线程,如果有则将任务分配给其中一个空闲的核心线程执行。如果没有空闲的核心线程或者核心线程的数量还没达到最大值,则创建一个新的核心线程来执行任务
  • 若核心线程已经达到最大值且工作队列未满,则将新提交的任务存储在这个工作队列中。如果工作队列已满,则会判断线程池中的线程数是否已经达到最大值,如果没有则创建一个新的非核心线程来执行任务
  • 若工作队列已满且线程池中的线程都已经处于工作状态,即核心线程和非核心线程都在执行任务,则交给饱和策略来处理这个任务。饱和策略可以决定如何处理无法处理的任务,例如抛出异常或者阻塞任务提交

线程池状态

  • RUNNING:该状态代表能接受新任务以及处理任务(初始状态)
  • SHUTDOWN:该状态代表不接受新任务,但处理已添加的任务(调用shutdown()时,由RUNNING->SHUTDOWN)
  • STOP:该状态时表示不接受新任务,不处理已添加任务,并会中断正在处理中的任务(调用shutdownNow()时,由RUNNING或者SHUTDOWN→STOP)
  • TIDYING:进入SHUTDOWN或者STOP状态后,所有任务都被处理或者清理干净后就会进入该状态,同时会执行terminated()方法(该方法是个钩子函数,自定义实现)
  • TERMINATED:结束状态,执行完terminated方法后由TIDYING->TERMINATED

Executor 框架

两级调度模型 

在HotSpotVM的线程模型中,Java线程(java.lang.Thread)被一对一映射为本地操作系统线程
Java线程启动时会创建一个本地操作系统线程;当该 Java 线程终止时,这个操作系统线程也会被回收。操作系统会调度所有线程并将它们分配给可用的CPU

  • 在上层架构中,Java多线程程序通常把应用分解为若干个任务,应用程序通过Executor框架将这些任务映射为固定数量的线程
  • 在下层架构中,操作系统内核将这些线程映射到硬件处理器上,下层的调度不受应用程序的控制

Executor结构

Executor框架包含的主要的类与接口如图所示

  • Executor是一个接口,它是Executor框架的基础,它将任务的提交与任务的执行分离开来
  • ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务
  • ScheduledThreadPoolExecutor是一个实现类,用来延迟之后执行任务或者定时执行任务。ScheduledThreadPoolExecutor比Timer更灵活,功能更强大
  • 实现Future接口的FutureTask类,代表异步计算的结果
  • Runnable接口和Callable接口的实现类,都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行

ThreadPoolExecutor

构造函数详细说明

public ThreadPoolExecutor(int corePoolSize,
						  int maximumPoolSize,
						  long keepAliveTime,
						  TimeUnit unit,
						  BlockingQueue<Runnable> workQueue,
						  ThreadFactory threadFactory,
						  RejectedExecutionHandler handler) {
	if (corePoolSize < 0 ||
		maximumPoolSize <= 0 ||
		maximumPoolSize < corePoolSize ||
		keepAliveTime < 0)
		throw new IllegalArgumentException();
	if (workQueue == null || threadFactory == null || handler == null)
		throw new NullPointerException();
	this.acc = System.getSecurityManager() == null ?
			null :
			AccessController.getContext();
	this.corePoolSize = corePoolSize;
	this.maximumPoolSize = maximumPoolSize;
	this.workQueue = workQueue;
	this.keepAliveTime = unit.toNanos(keepAliveTime);
	this.threadFactory = threadFactory;
	this.handler = handler;
}

参数介绍:

  • corePoolSize:核心线程池个大小。一般来说任务比较耗时可以配CPU核数*2,因为这样可以充分利用CPU,任务小并且执行很快则可以配CPU核数+1或者更小(因为线程上下文切换耗时)(获取CPU核心数:Runtime.getRuntime().availableProcessors())
  • maximumPoolSize:最大线程池大小。当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务,总线程数≤maximumPoolSize
  • keepAliveTime:空闲时间,超过核心线程数的线程在到达空闲时间后会被销毁
  • TimeUnit : 时间单位
  • BlockingQueue:用来暂时保存任务的队列(阻塞队列)
  • ThreadFactory:自定义的线程工厂,默认是一个新的、非守护线程并且不包含特殊的配置信息,我们也可以自定义加入我们的调试信息,比如线程名称、错误日志等
  • RejectedExecutionHandler:饱和策略。当线程数=maximumPoolSize,且任务队列已满时,多余的任务需要采取的措施,有以下几种(默认AbortPolicy):
    • AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
    • DiscardPolicy: 丢掉这个任务并且不会有任何异常
    • DiscardOldestPolicy:丢弃最老的。也就是说如果队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列
    • CallerRunsPolicy:主线程会自己去执行该任务,不会等待线程池中的线程去执行
    • 自定义:当然也可以自定义策略

常用ThreadPoolExecutor类型

ThreadPoolExecutor通常使用工厂类Executors来创建,包括3种ThreadPoolExecutor类型:

  • FixedThreadPool:可重用固定线程数的线程池
  • SingleThreadExecutor:单个线程的线程池(只有一个工作线程)
  • CachedThreadPool:根据需要创建新线程的线程池
FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
	return new ThreadPoolExecutor(nThreads, nThreads,
								  0L, TimeUnit.MILLISECONDS,
								  new LinkedBlockingQueue<Runnable>());
}
  • 其中corePool和maximumPoolSize 都被设置成指定的参数
  • keepAliveTime设置为0L,意味着多余的空闲线程会被立即终止
  • 适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场景,更适合于负载比较重的服务器
fixedThreadPool执行流程
  • 如果当前线程少于corePool,则创建新线程来执行当前任务
  • 当运行的线程数等于corePool之后,将任务加入LinkedBlockingQueue队列中
  • 线程执行完当前任务后会循环反复从LinkedBlockingQueue获取任务来执行

由于为LinkedBlockingQueue无界队列(长度Integer.MAX_VALUE),所以会出现如下情景:

  • maximumPoolSize和keepAliveTime参数将会无效,因为maximumPoolSize=corePool
  • 不会拒绝任务,因为是无界队列,任务不会满
SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
	return new FinalizableDelegatedExecutorService
		(new ThreadPoolExecutor(1, 1,
								0L, TimeUnit.MILLISECONDS,
								new LinkedBlockingQueue<Runnable>()));
}

corePool和maximumPoolSize 均被设置成了1,其他影响和运行方式都与FixedThreadPool相同

CachedThreadPool
public static ExecutorService newCachedThreadPool() {
	return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
								  60L, TimeUnit.SECONDS,
								  new SynchronousQueue<Runnable>());
}
  • corePoolSize核心线程数为0,而maximumPoolSize为Integer.MAX_VALUE(21亿多),意味着没有空闲线程就会不断的创建线程去执行,极端情况会耗尽CPU和内存资源;
  • keepAliveTime=60s后空闲线程会被终止,所以长时间内保持空闲的情况下不会占用任何资源
  • SynchronousQueue是没有容量的阻塞队列,每个插入操作都会等待另一个线程对应的取出操作
  • 适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器

ScheduleThreadPoolExecutor

构造函数

public ScheduledThreadPoolExecutor(int corePoolSize,
								   RejectedExecutionHandler handler) {
	super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
		  new DelayedWorkQueue(), handler);
}

ScheduledThreadPoolExecutor为了实现周期性任务对ThreadPoolExecutor做了如下修改:

  • 使用DelayedWorkQueue作为任务队列
  • 获取任务的方式不同,同样都是队列的take,但增加了时间的判断
  • 执行周期任务后,增加了额外的处理(需要把任务重新添加进队列)

常用ScheduleThreadPoolExecutor类型

ScheduledThreadPoolExecutor通常使用工厂类Executors来创建,2种类型:

  • ScheduledThreadPoolExecutor: 包含若干个线程 ScheduledThreadPoolExecutor
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) 
    • ScheduledThreadPoolExecutor 适用于需要多个后台线程执行周期任务,同时为了满足资源管理的需求而需要限制后台线程的数量的应用场景

  • SingleThreadScheduledExecutor: 只包含一个线程 ScheduledThreadPoolExecutor
    public static ScheduledExecutorService newSingleThreadScheduledExecutor()
    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
    • SingleThreadScheduledExecutor 适用于需要单个后台线程执行周期任务,同时需要保证顺序地执行各个任务的应用场景

FutureTask

Future接口和实现Future接口的FutureTask类用来表示异步计算的结果。当我们把Runnable接口或Callable接口的实现类提交(submit)给 ThreadPoolExecutor或ScheduledThreadPoolExecutor时,ThreadPoolExecutor或ScheduledThreadPoolExecutor会向我们返回一个FutureTask对象

Executor 实践

ThreadPoolExecutor

package com.bierce;
import java.util.concurrent.*;
public class TestThreadPoolExecutor {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        //方式一:Runnable方式的分配10个任务提交给线程池
        ThreadPoolDemo threadPoolDemo = new ThreadPoolDemo();
        for (int i = 0; i <= 10; i++) {
            executorService.submit(threadPoolDemo);
        }
        //方式二:Callable方式的分配10个任务提交给线程池
        for (int i = 0; i <= 10; i++) {
            Future<Object> sum = executorService.submit(() -> {
                int sum1 = 0;
                for (int i1 = 1; i1 <= 100; i1++) {
                    sum1 += i1;
                }
                return sum1;
            });
            System.out.println(Thread.currentThread().getName() + ":" + sum.get()); //main:5050
        }
        //关闭线程池
        executorService.shutdown();
    }
}
class ThreadPoolDemo implements Runnable{
    private int i = 0;
    @Override
    public void run() {
        while (i<10){
            System.out.println(Thread.currentThread().getName() + ":" + i++);
        }
    }
}

ScheduleThreadPoolExecutor

package com.bierce;
import java.util.Random;
import java.util.concurrent.*;
public class TestScheduleThreadPool {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        for (int i = 1; i <=5 ; i++) {
            //调用schedule方法执行任务
            Future<Integer> random = scheduledExecutorService.schedule(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int random = new Random().nextInt(100);
                    System.out.println(Thread.currentThread().getName() + ":" + random);
                    return random;
                }
            },1,TimeUnit.SECONDS); //每隔一秒执行一个任务
            System.out.println(random.get());
        }
        scheduledExecutorService.shutdown(); //关闭线程池
    }
}

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

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

相关文章

机器学习 | Python实现KNN(K近邻)模型实践

机器学习 | Python实现KNN(K近邻)模型实践 目录 机器学习 | Python实现KNN(K近邻)模型实践基本介绍模型原理源码设计学习小结参考资料基本介绍 一句话就可以概括出KNN(K最近邻算法)的算法原理:综合k个“邻居”的标签值作为新样本的预测值。更具体来讲KNN分类过程,给定一个训…

Android APK体积优化(瘦身)

1、基础知识&#xff1a; 1.1 apk结构 lib &#xff1a;存放so文件&#xff0c;对应不同的cpu架构 res &#xff1a;资源文件&#xff0c;layout、drawable等&#xff0c;经过aapt编译 assets &#xff1a;资源文件&#xff0c;不经过aapt编译 classes.dex &#xff1a;dx编译…

基于HTML+CSS+Echarts大屏数据可视化集合共99套

基于HTMLCSSEcharts大屏数据可视化集合共99套 一、介绍二、展示1.大数据展示系统2.物流订单系统3.物流信息系统4.办税渠道监控平台5.车辆综合管控平台 三、其他系统实现四、获取源码 一、介绍 基于HTML/CSS/Echarts的会议展览、业务监控、风险预警、数据分析展示等多种展示需求…

菜单和内容滚动的联动原理及代码

之前写代码有个需求&#xff1a;左侧是一个菜单&#xff0c;右边是内容&#xff0c;点击左侧菜单右边内容滚动到对应位置&#xff0c;右边内容滚动到某位置时&#xff0c;左侧菜单也会选中对应的菜单项。UI如下&#xff1a;这是大多网站的移动端都会有的需求。 解决方案一&…

Spring Boot业务代码中使用@Transactional事务失效踩坑点总结

1.概述 接着之前我们对Spring AOP以及基于AOP实现事务控制的上文&#xff0c;今天我们来看看平时在项目业务开发中使用声明式事务Transactional的失效场景&#xff0c;并分析其失效原因&#xff0c;从而帮助开发人员尽量避免踩坑。 我们知道 Spring 声明式事务功能提供了极其…

python3装饰器理解与实战

前言 装饰器本质上是一个Python函数&#xff0c;它可以让其他函数在不需要做任务代码变动的前提下增加额外功能&#xff0c;装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景&#xff0c;比如&#xff1a;插入日志、性能测试、事务处理、缓存、权限校验等场景。装…

R语言生存分析(机器学习)(2)——Enet(弹性网络)

弹性网络&#xff08;Elastic Net&#xff09;:是一种用于回归分析的统计方法&#xff0c;它是岭回归&#xff08;Ridge Regression&#xff09;和lasso回归&#xff08;Lasso Regression&#xff09;的结合&#xff0c;旨在克服它们各自的一些限制。弹性网络能够同时考虑L1正则…

YARN框架和其工作原理流程介绍

目录 一、YARN简介 二、YARN的由来 三、YARN的基本设计思想 四、YARN 的基本架构 4.1 基本架构图 4.2 基本组件介绍 4.2.1 ResourceManager 4.2.1.1 任务调度器(Resource Scheduler) 4.2.1.2 应用程序管理器&#xff08;Applications Manager&#xff09; 4.2.1.3 其他…

MongoDB(三十九)

目录 一、概述 &#xff08;一&#xff09;相关概念 &#xff08;二&#xff09;特性 二、应用场景 三、安装 &#xff08;一&#xff09;编译安装 &#xff08;二&#xff09;yum安装 1、首先制作repo源 2、软件包名&#xff1a;mongodb-org 3、启动服务&#xff1a…

2023国赛数学建模A题思路分析

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 全国大学生数学建模…

SQL Server2019安装后使用SQL Server身份验证登录失败

错误情况 今天在电脑安装SQL Server2019和SMMS&#xff0c;安装过程一切顺利&#xff0c;但是在使用SMMS连接数据库时出现了异常。使用"Window 身份验证"登录时正常&#xff0c;但是如果改为使用"SQL Server 身份验证"登录时却连接失败&#xff01; 解决方…

Tomcat多实例部署及nginx+tomcat的负载均衡和动静分离

Tomcat多实例部署 安装 jdk、tomcat&#xff08;流程可看之前博客&#xff09; 配置 tomcat 环境变量 [rootlocalhost ~]# vim /etc/profile.d/tomcat.sh#tomcat1 export CATALINA_HOME1/usr/local/tomcat/tomcat1 export CATALINA_BASE1/usr/local/tomcat/tomcat1 export T…

【计算机网络】TCP协议超详细讲解

文章目录 1. TCP简介2. TCP和UDP的区别3. TCP的报文格式4. 确认应答机制5. 超时重传6. 三次握手7. 为什么两次握手不行?8. 四次挥手9. 滑动窗口10. 流量控制11. 拥塞控制12. 延时应答13. 捎带应答14. 面向字节流15. TCP的连接异常处理 1. TCP简介 TCP协议广泛应用于可靠性要求…

Openlayers 实战 - 地图视野(View)- 图层 -(layer)- 资源(source)显示等级设置。

Openlayers 实战 - 地图视野&#xff08;View&#xff09;- 图层 -&#xff08;layer&#xff09;- 资源&#xff08;source&#xff09;显示等级设置。 问题原因核心代码完整代码&#xff1a;在线示例 在以往的项目维护中&#xff0c;出现一个问题&#xff0c;使用最新高清底图…

error_Network Error

此页面为订单列表&#xff0c;是混合开发(页面嵌入在客户端中) 此页面为订单列表&#xff0c;此需求在开发时后端先将代码发布在测试环境&#xff0c;我在本地调试时调用的后端接口进行联调没有任何问题。 此后我将代码发布在测试环境&#xff0c;在app中打开页面&#xff0c…

JS逆向系列之某多多 anti_content

文章目录 声明目标网址anti_content参数分析参考js 环境python 调用测试往期逆向文章推荐声明 本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请私信我立即删除! 目标网址 aHR0cHM6Ly9tb2JpbGUueWFuZ2tlZHVvL…

【碎碎念随笔】1、回顾我的电脑和编程经历

✏️ 闲着无事&#xff0c;讲述一下我的计算机和代码故事 一、初识计算机 &#x1f5a5;️ 余家贫&#xff0c;耕植无钱买电脑。大约六年级暑假&#xff0c;我在姐姐哪儿第一次接触到了计算机&#xff08;姐姐也是买的二手&#xff09;。 &#x1f5a5;️ 计算机真有趣&#x…

第二章:CSS基础进阶-part1:CSS高级选择器

文章目录 一、 组合选择器二、属性选择器三、伪类选择器1、动态伪类选择器2、状态伪类选择器3、结构性伪类选择器4、否定伪类选择器 一、 组合选择器 后代选择器&#xff1a;E F子元素选择器&#xff1a; E>F相邻兄弟选择器&#xff1a;EF群组选择器&#xff1a;多个选择器…

电脑提示数据错误循环冗余检查怎么办?

有些时候&#xff0c;我们尝试在磁盘上创建分区或清理硬盘时&#xff0c;还可能会遇到这个问题&#xff1a;数据错误循环冗余检查。这是如何导致的呢&#xff1f;我们又该如何解决这个问题呢&#xff1f;下面我们就来了解一下。 导致冗余检查错误的原因有哪些&#xff1f; 数据…

Vue3 setup tsx 子组件向父组件传值 emit

需求&#xff1a;Vue3 setup 父组件向子组件传值&#xff0c;子组件接收父组件传入的值&#xff1b;子组件向父组件传值&#xff0c;父组件接收的子组件传递的值。 父组件&#xff1a;parent.tsx&#xff1a; import { defineComponent, ref, reactive } from vue; import To…