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…

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

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

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;可同时播放多…

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…

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

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

缓存淘汰算法(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(./汪学长的随堂资…

使用chatGPT-4 畅聊量子物理学(三)

集合了人类智慧的照片&#xff0c;来自 1927 年举行的第五届索尔维国际会议。 Omer 什么是“物理系统在被测量之前不具有确定的属性。量子力学只能预测给定测量的可能结果的概率分布" ChatGPT 这句话描述了量子力学中的一种基本原则&#xff0c;即“物理系统在被测量之前…

世微AP2813 平均电流双路降压恒流驱动器 LED储能电源驱动指示灯IC 可恒流可爆闪 可双路恒流

产品描述 AP2813 是一款双路降压恒流驱动器,高效率、外围简单、内置功率管&#xff0c;适用于 5-80V 输入的高精度降压 LED 恒流驱动芯片。内置功率管输出最大功率可达12W&#xff0c;最大电流 1.2A。AP2813 一路直亮&#xff0c;另外一路通过 MODE1 切换全亮&#xff0c;爆闪…

利用OpenCV光流算法实现视频特征点跟踪

光流简介 光流&#xff08;optical flow&#xff09;是运动物体在观察成像平面上的像素运动的瞬时速度。光流法是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系&#xff0c;从而计算出相邻帧之间物体的运动信息的一种方法。…

大模型PEFT技术原理(二):P-Tuning、P-Tuning v2

随着预训练模型的参数越来越大&#xff0c;尤其是175B参数大小的GPT3发布以来&#xff0c;让很多中小公司和个人研究员对于大模型的全量微调望而却步&#xff0c;近年来研究者们提出了各种各样的参数高效迁移学习方法&#xff08;Parameter-efficient Transfer Learning&#x…

css鼠标样式 cursor: pointer

cursor: none; cursor:not-allowed; 禁止选择 user-select: none; pointer-events:none;禁止触发事件, 该样式会阻止默认事件的发生&#xff0c;但鼠标样式会变成箭头

Hlang社区-前端社区宣传首页实现

文章目录 前言页面结构固定钉头部轮播JS特效完整代码总结前言 这里的话,博主其实也是今年参与考研的大军之一,所以的话,是抽空去完成这个项目的,当然这个项目的肯定是可以在较短的时间内完成的。 那么废话不多说,昨天也是干到1点多,把这个首页写出来了。先看看看效果吧:…

断点续传的未来发展趋势与前景展望

断点续传是一种在网络传输中断后&#xff0c;能够从中断的位置继续传输的技术。它可以有效地避免因为网络不稳定、服务器故障、用户操作等原因导致的传输失败&#xff0c;节省了用户的时间和流量&#xff0c;提高了传输的效率和可靠性。断点续传在很多场景中都有广泛的应用&…

AI 绘画Stable Diffusion 研究(八)sd采样方法详解

大家好&#xff0c;我是风雨无阻。 本文适合人群&#xff1a; 希望了解stable Diffusion WebUI中提供的Sampler究竟有什么不同&#xff0c;想知道如何选用合适采样器以进一步提高出图质量的朋友。 想要进一步了解AI绘图基本原理的朋友。 对stable diffusion AI绘图感兴趣的朋…

手撕LFU缓存

手撕LRU缓存_右大臣的博客-CSDN博客 是LRU的升级&#xff0c;多了一个访问次数的维度 实现 LFUCache 类&#xff1a; LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象int get(int key) - 如果键 key 存在于缓存中&#xff0c;则获取键的值&#xff0c;否则返…