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;@Overridepublic 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>() {@Overridepublic 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(); //关闭线程池}
}

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

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

相关文章

Jmeter - 函数助手

目录 __StringFromFile __CSVRead __counter __RandomString __StringFromFile StringFromFile函数用于获取文本文件的值&#xff0c;一次读取一行 1、输入文件的全路径&#xff1a;填入文件路径 2、存储结果的变量名&#xff08;可选&#xff09; 3、Start file sequence …

Tomcat+Http+Servlet

文章目录 1.HTTP1.1 请求和响应HTTP请求&#xff1a;请求行请求头请求体HTTP响应&#xff1a;响应行&#xff08;状态行&#xff09;响应头响应体 2. Apache Tomcat2.1 基本使用2.2 IDEA中创建 Maven Web项目2.3 IDEA中使用Tomcat 3. Servlet3.1 Servlet快速入门3.2 Servlet执行…

游戏中的UI适配

引用参考&#xff1a;感谢GPT UI适配原理以及常用方案 游戏UI适配是确保游戏界面在不同设备上以不同的分辨率、屏幕比例和方向下正常显示的关键任务。下面是一些常见的游戏UI适配方案&#xff1a; 1.分辨率无关像素&#xff08;Resolution-Independent Pixels&#xff09;&a…

CentOS 8 安装 oracle 23c CentOS9 Error deal

1.环境准备 软件准备 序号 软件 下载地址 1 VirtualBox https://www.virtualbox.org/wiki/Downloads2 CentOS Stream 8 https://mirrors.tuna.tsinghua.edu.cn/centos/8-stream/isos/x86_64/CentOS-Stream-8-x86_64-latest-dvd1.iso3 oracle-database-free-23c # cd ~/Down…

Vue 3 中定义组件常用方法

在Vue 3 中有多种定义组件的方法。从选项到组合再到类 API&#xff0c;情况大不相同 1、方式一&#xff1a;Options API 这是在 Vue 中声明组件的最常见方式。从版本 1 开始可用&#xff0c;您很可能已经熟悉它。一切都在对象内声明&#xff0c;数据在幕后由 Vue 响应。它不是…

C语言实现快速排序

什么是快速排序&#xff1f; 快速排序&#xff08;Quick Sort&#xff09; 是一种高效的分治法排序算法。它通过选择一个基准元素&#xff0c;将数组分成小于基准的部分和大于基准的部分&#xff0c;然后递归地对这些部分进行排序&#xff0c;最终将它们合并起来&#xff0c;完…

【广州华锐视点】VR警务教育实训系统模拟真实场景进行实践训练

随着科技的发展&#xff0c;虚拟现实技术在教育领域得到了广泛的应用。VR警务教育实训系统就是其中的一种应用&#xff0c;该系统由广州华锐互动开发&#xff0c;可以模拟真实的警务场景&#xff0c;让学生通过虚拟现实技术进行实践训练&#xff0c;提高学生的实践能力和技能水…

.NET6使用微信小程序授权登录,获取手机号

1.在appsettings配置你的小程序配置信息 //微信小程序信息配置"WechatConfig": {"appid": "", //小程序ID"secret": "" //小程序秘钥},2.请求接口时先获取Access_token #region 获取小程序的Access_tokenpublic object GetA…

Linux:shell脚本循环语句

目录 一、循环含义 二、echo命令 三、for 3.1.将1到100累加求和 3.2批量添加用户 3.3 根据IP地址检查主机状态 四、 while 和 until 4.1 猜价格 4.2 1-100求和 一、循环含义 循环含义 将某代码段重复运行多次&#xff0c;通常有进入循环的条件和退出循环的条件 重复…

视频汇聚平台EasyCVR视频监控播放平台WebRTC流地址无法播放的问题解决方案

开源EasyDarwin视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多…

Linux的ln命令

ln是link的缩写,在Linux中 ln 命令的功能是为某一个文件在另外一个位置建立一个同步的链接&#xff0c;当我们需要在不同的目录&#xff0c;用到相同的文件时&#xff0c;我们不需要在每一个需要的目录下都放一个必须相同的文件&#xff0c;我们只要在某个固定的目录&#xff0…

Ubuntu18.04.4裸机配置

下载虚拟机Ubuntu18.04.4 链接&#xff1a;https://pan.baidu.com/s/1jyucyUSXa9-Fw9ctuU87hA 提取码&#xff1a;o42a –来自百度网盘超级会员V5的分享 VMware选择镜像安装 设置你的用户名&#xff0c;就像windows上登录用户一样简单 下一步……下一步……如此简单 下载…

Floyd(多源汇最短路)

Floyd求最短路 给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;边权可能为负数。 再给定 k 个询问&#xff0c;每个询问包含两个整数 x 和 y&#xff0c;表示查询从点 x 到点 y 的最短距离&#xff0c;如果路径不存在&#xff0c;则输出 impo…

每日一题 33搜素旋转排序数组(二分)

题目 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], ..., nums[n-1], nums[0], nums[…

Fortinet数据中心防火墙及服务ROI超300%!Forrester TEI研究发布

近日&#xff0c;专注网络与安全融合的全球网络安全领导者 Fortinet&#xff08;NASDAQ&#xff1a;FTNT&#xff09;联合全球知名分析机构Forrester发布总体经济影响独立分析报告&#xff0c;详细阐述了在企业数据中心部署 FortiGate 下一代防火墙&#xff08;NGFW&#xff09…

Django图书商城系统实战开发-实现商品管理

Django图书商城系统实战开发 - 实现商品管理 在本教程中&#xff0c;我们将使用Django框架来实现一个简单的图书商城系统&#xff0c;并重点讨论如何实现商品管理功能。此外&#xff0c;我们还将介绍如何使用Markdown格式来写博客&#xff0c;并将其集成到我们的图书商城系统中…

缓存淘汰算法(LFU LRU FIFO)及进程的状态和转换

目录 一、缓存淘汰算法 1.LFU&#xff08;Least Frequently Used&#xff09;最近最不常用算法 2.LRU&#xff08;Least Recently User&#xff09;最近最少使用算法 3.FIFO&#xff08;First in first out&#xff09;先进先出算法 二、进程的状态和转换 1.最基本的三种状…

OpenCV图像处理——模版匹配和霍夫变换

目录 模版匹配原理实现 霍夫变换霍夫线检测 模版匹配 原理 实现 rescv.matchTemplate(img,template,method)import numpy as np import cv2 as cv import matplotlib.pyplot as pltimgcv.imread(./汪学长的随堂资料/6/模板匹配/lena.jpg) templatecv.imread(./汪学长的随堂资…

UniApp 使用命令创建页面的详细指南

系列文章目录 文章目录 系列文章目录前言一、安装Uni-CLI二、创建页面三、页面创建命令四、页面结构五、页面使用总结 前言 UniApp是一款跨平台的前端框架&#xff0c;可以用于开发同时运行在多个平台&#xff08;如微信小程序、H5、App等&#xff09;的应用程序。本文将详细介…

系统架构设计师---考试通关练习题

第一章 系统架构设计师概述 1 .以下()不是现代信息系统的架构的三个要素。 A.构件 B.模式 C.规划 D.属性 解析:现代信息系统的架构有三个要素,即构件、模式和规划。 答案:D 2. 软件系统架构是关于软件系统的结构、行为和()的高级抽象。 A.构件 B.模式 C…