一文带你清晰弄明白线程池的原理

article/2025/10/7 10:12:56

不知道你是否还记得阿里巴巴的java代码规范中对多线程有这样一条强制规范:
【强制】线程资源必须通过线程池提供,不允许在程序中显示创建线程。
说明:使用线程池的好处是减少在创建和销毁线程池上所消耗的时间以及系统资源的开销,解决资源不足的问题,如果不适用线程池, 有可能造成系统创建大量同类线程而导致消耗完内存或者“过渡切换”的问题。

这条强制性规范也说明了使用线程池主要是解决一下两个问题:
(1)提升性能:线程池能独立负责线程的创建、维护和分配。在执行大量一部任务时,可以不需要自己创建线程,而是将任务交给线程池去调度。线程池能尽可能使用空闲的线程去执行异步任务,最大限度地对已经创建的线程进行复用,使得性能提升明显。
(2)线程管理: 每个java线程池会保持一些基本的线程统计信息, 以便对线程进行有效管理,使得能对所接收到异步任务进行高效调度。

JUC 的线程架构

JUC 是java.util.concurrent工具包的简称, 是从JDK1.5开始加入JDK的,用于完成高并发、处理多线程的一个工具包。 JUC 的线程架构如下图所示:
在这里插入图片描述
Executor是java 异步目标任务的“执行者”接口,其目的是执行目标任务。

ExecutorService继承于Executor。 它是java异步目标任务的“执行者服务接口”,对外提供异步任务的接收服务。

AbstractExecutorService 是一个抽象类,它的目的是为ExecutorService 中的接口提供默认实现。

ThreadPoolExecutor 是线程池的实现类, 是JUC线程池的核心实现类。

ScheduledExecutorService 是一个接口,可以完成延时和周期性任务调度线程池的接口,其功能类似Timer/TimerTask、

ScheduledThreadPoolExecutor提供了ScheduledExecutorService 线程池接口中“延迟执行”和周期执行等抽象调度方法的具体实现。在高并发程序中, ScheduledThreadPoolExecutor 的性能由于Timer。

Executors

Executors 是一个静态工厂类,它通过静态工厂方法返回ExecutorService、ScheduledExecutorService等线程池示例对象。 java通过Executors 工厂类可以提供4种快捷创建线程池的方法。如下图所示:
在这里插入图片描述
但是在阿里巴巴的java代码规范中,有这样一条强制规范:
【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor 的方式。
说明: Executors返回的线程池对象的弊端如下:
(1)FixedThreadPool 和SingleThreadPool 运行的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM;
(2)CacheThreadPool 和ScheduledThreadPool 允许的创建线程数量为Integer.MAX_VALUE,可能 会创建大量的线程,从而导致OOM。

那么我们使用ThreadPoolExecutor 如何创建线程池呢,

ThreadPoolExecutor创建线程池

我们通过标准构造器ThreadPoolExecutor去构造工作线程池,Executors工厂类中创建线程池的快捷工厂方法实际上是调用TheadPoolExecutor(定时任务使用ScheduledThreadPoolExecutor)线程池的构造方法完成的, 构造方法如下:

在这里插入图片描述
在这里插入图片描述
其中线程池执行器会根据corePoolSize和maximumPoolSize自动维护线程池中的工作线程,规则如下:
在这里插入图片描述
在这里插入图片描述

向线程池提交任务方式

向线程池提交任务的两种方式分别是execute()方法和submit()方法.
execute() 方法:
在这里插入图片描述

submit()方法:
在这里插入图片描述
从接口可以得出submit()和executor()方法的区别是:

  • Execute()方法只能接收Runnable类型的参数,而submit()方法可以接收Callable,Runnale两种类型的类型, Callable类型的任务是可以返回执行结果的, Runnable类型的任务不可以返回执行结果.
  • submit()提交任务后会返回值,而execute()没有
  • submit()方便Exception处理.

线程池的任务调度流程

线程池的任务调度流程如下图所示:
在这里插入图片描述
在创建线程池的是,如果线程池的参数配置不合理,就会出现任务不能被正常调度的问题.

总结:
核心和最大线程数量,BlockingQueue队列等参数如果配置得不合理,可能会造成异步任务得不到预期的并发执行, 造成严重的排队等待现象.

想城池的调度器创建线程的一条重要的规则:在核心线程数已满后,还需要等待阻塞队列已满,才会去创建新的线程.

线程池的拒绝策略

当线程池的任务缓存队列为有界队列且阻塞队列已满的情况,提交任务到线程池的时候就会被拒绝, 当线程池已经被关闭了, 提交任务到线程池也会被拒绝. 无论是哪一种情况任务被拒绝, 线程池都会调用RejectedExecutionHandler拒绝策略接口的实例的rejectedExecution方法, 拒绝策略的实现有以下5中:

  • AbortPolicy-拒绝策略: 如果线程池队列已满,新任务就会被拒绝,并且抛出RejectedExecutionException异常, 该策略是线程池默认的拒绝策略.
  • DiscardPolicy-抛弃策略: 如果线程池队列已满, 新任务就会直接被丢掉, 并且不会有任何异常抛出.
  • DiscardOldestPolicy-抛弃最老任务策略: 如果队列满了,就会将最早进入队列的任务抛弃,从队列中腾出空间,在尝试加入队列.
  • CallerRunsPolicy-调用者执行策略:在新任务被添加到线程池时,如果添加失败,那么提交任务线程会自己去执行该任务,不会使用线程池中的线程去执行新任务.
  • 自定义策略.实现RejectedExecutionHandler接口的rejectedExecution方法即可.

线程池关闭

一般情况下, 线程池启动后建议手动关闭,可以结合shutdown(),shutdownNow(),awaitTermination() 三个方法优雅地关闭一个线程池, 关闭步骤如下:
(1) 执行shutdow()方法,拒绝新任务的提交,并等待所有任务有序地执行完毕.

(2) 执行awaitTermination(Long timeout,TimeUnit unit)方法,指定超时时间,判断是否已经关闭所有任务,线程池关闭完成.

(3)如果awaitTermination()方法返回false,或者被中断,就调用shutDownNow()方法立即关闭线程池所有任务.

(4)补充执行awaitTermination(Long timeout,TimeUnit unit)方法,判断线程池是否关闭完成. 如果超时,就可以进入循环关闭,循环一定的次数,不断关闭线程池,直到其关闭或者循环结束.

关闭线程池代码如下:

public  static void shutdownThreadPoolGrecefully(ExecutorService threadPool){if (!(threadPool instanceof  ExecutorService)|| threadPool.isTerminated()){return;}try {//拒绝接收新任务threadPool.shutdown();}catch (SecurityException e){return;}catch (NullPointerException e){return;}try {//等待60s,等待线程池中的任务完成执行if(!threadPool.awaitTermination(60, TimeUnit.SECONDS)){//调用shutdowNow 取消正在执行的任务threadPool.shutdownNow();//再次等待60s ,如果还未结束,可以再次尝试,或则直接放弃if(!threadPool.awaitTermination(60,TimeUnit.SECONDS)){System.out.println("线程池任务未正常完成执行结束");}}} catch (InterruptedException e) {// 捕获异常,重新调用shutdowNowthreadPool.shutdownNow();}// 仍然没有关闭,循环关闭1000次,每次等嗲10毫秒if (!threadPool.isTerminated()){try {for (int i=0;i<1000;i++){if (threadPool.awaitTermination(10,TimeUnit.SECONDS)){break;}threadPool.shutdownNow();}} catch (InterruptedException e) {System.out.println(e.getMessage());}catch (Throwable e){System.out.println(e.getMessage());}}}

除了以上的关闭线程池方法以外,还可以在JVM中注册一个钩子函数,在JVM进程关闭之前,由钩子函数自动将线程池关闭,以确保资源正常释放,代码如下:

  //懒汉式单例创建线程池:用于执行定时/顺序任务static class SeqOrScheduledTargetThreadPoolLazyHolder{//线程池:用于定时任务/顺序排队执行任务static  final  ScheduledThreadPoolExecutor EXECUTOR= new ScheduledThreadPoolExecutor(1,new CustomThreadFactory("seq"));static {//注册JVM关闭时的钩子函数Runtime.getRuntime().addShutdownHook(new ShutdownHookThread("定时和顺序任务线程池",new Callable<Void>(){@Overridepublic Void call() throws Exception {//关闭线程池shutdownThreadPoolGrecefully(EXECUTOR);return null;}}));}}

到此为止,线程池的原理您明白了吗?


http://chatgpt.dhexx.cn/article/JL5FJhRN.shtml

相关文章

线程池工作原理

一、线程池默认工作流程 1、线程在有任务的时候会创建核心的线程数corePoolSize 2、当线程满了&#xff08;有任务但是线程被使用完&#xff09;不会立即扩容,而是放到阻塞队列中,当阻塞队列满了之后才会继续创建线程。 3、如果队列满了,线程数达到最大线程数则会执行拒绝策…

线程池的工作原理

线程池&#xff0c;就是存放线程的池子&#xff0c;池子里存放了很多可以复用的线程 作用&#xff1a; 1.对线程进行统一管理 2.降低系统资源消耗。通过复用已存在的线程&#xff0c;降低线程创建和销毁造成的消耗 3.提高响应速度。当有任务到达时&#xff0c;无需等待新线…

线程池核心原理分析

一、基础概念 线程池是一种多线程开发的处理方式&#xff0c;线程池可以方便得对线程进行创建&#xff0c;执行、销毁和管理等操作。主要用来解决需要异步或并发执行任务的程序 谈谈池化技术 简单点来说,就是预先保存好大量的资源,这些是可复用的资源,你需要的时候给你。对于…

线程池原理(讲的非常棒)

Java并发编程&#xff1a;线程池的使用 在前面的文章中&#xff0c;我们使用线程的时候就去创建一个线程&#xff0c;这样实现起来非常简便&#xff0c;但是就会有一个问题&#xff1a; 如果并发的线程数量很多&#xff0c;并且每个线程都是执行一个时间很短的任务就结束了&…

服务器常见的网络攻击以及防御方法

网络安全威胁类别 网络内部的威胁&#xff0c;网络的滥用&#xff0c;没有安全意识的员工&#xff0c;黑客&#xff0c;骇客。 木马攻击原理 C/S 架构&#xff0c;服务器端被植入目标主机&#xff0c;服务器端通过反弹连接和客户端连接。从而客户端对其进行控制。 病毒 一…

传奇服务器容易受到什么攻击,怎么防御攻击?

有兄弟问明杰&#xff0c;说自己打算开服&#xff0c;听说攻击挺多的&#xff0c;就是想先了解一下开传奇用的服务器最容易受到什么类型的攻击&#xff0c;如果遇到了又改怎么防御呢&#xff1f;带着这个问题&#xff0c;明杰跟大家详细的说一下&#xff0c;常见的开区时候遇到…

传奇服务器常见的网络攻击方式有哪些?-版本被攻击

常见的网络攻击方式有哪些&#xff1f;常见的网络攻击方式 &#xff1a;端口扫描&#xff0c;安全漏洞攻击&#xff0c;口令入侵&#xff0c;木马程序&#xff0c;电子邮件攻击&#xff0c;Dos攻击。 1.传奇服务器端口扫描&#xff1a; 通过端口扫描可以知道被扫描计算机开放了…

服务器被ddos攻击的处置策略

如果您的服务器遭到了DDoS攻击&#xff0c;以下是一些可以采取的措施&#xff1a; 使用防火墙和安全组进行限制&#xff1a;限制服务器的流量以防止进一步的攻击。 升级服务器资源&#xff1a;为了应对更高的流量&#xff0c;可以升级服务器的内存&#xff0c;处理器等资源。 安…

如何从根本上防止服务器被攻击

随着互联网的发展&#xff0c;服务器成为了企业和个人网络应用的重要基础设施。但是&#xff0c;随之而来的网络安全威胁也在不断增加&#xff0c;服务器安全问题也成为了影响企业信息安全的重要因素。针对这种情况&#xff0c;服务器安全防御变得尤为重要。   服务器安全防御…

来一起学怎么攻击服务器吧!!!

我们上线了两门新课。 一门教你漏洞的原理和攻击代码怎么写。 一门会教你木马的制作。 一如既往地&#xff0c;新课刚上线都是限时优惠状态&#xff0c;感兴趣的同学用力点击下面这两个妹子吧&#xff1a; Kali 渗透测试 - 服务器攻击实战 Kali 渗透测试 - 后门技术实战 提示&a…

网站服务器怎么做防御?遇到攻击如何解决?

每个网站都可能遇到网络攻击&#xff0c;这是正常现象。攻击可能来自密码错误、防火墙配置错误、病毒软件、空闲端口等。大量的网络攻击在竞争激烈的行业或企业中更为常见。 目前需要有一套完整的安全策略来保护流浪数据&#xff0c;防止网站服务器被攻击。完善可靠的安全策略…

服务器被攻击怎么办?常见处理方法

对于企业用户来&#xff0c;最害怕的莫过于服务器遭受攻击了&#xff0c;比如被大量登录、网页被篡改、数据库被非法登录、网络日志异常等&#xff0c;尽管运维人员在此之前做好了全面的防范工作&#xff0c;但攻击再所难免&#xff0c;如何在服务器遭受攻击后能够迅速有效的处…

服务器被DDoS攻击,怎么破?

文章目录 前言网站受到DDoS的症状判断是否被攻击查看网络带宽占用查看网络连接TCP连接攻击SYN洪水攻击 防御措施TCP/IP内核参数优化iptables 防火墙预防防止同步包洪水&#xff08;Sync Flood&#xff09;Ping洪水攻击&#xff08;Ping of Death&#xff09;控制单个IP的最大并…

黑客攻击入侵服务器的6种常见方式

服务器被入侵既有一定的偶然性&#xff0c;也有一定的必然性&#xff0c;网络上有两种黑客&#xff0c;一种是漫无目的的漫天撒网形式的黑客&#xff0c;一种是目标明确只入侵指定目标的黑客&#xff0c;我们称前一种黑客叫菜鸟黑客&#xff0c;称后一种黑客叫高级黑客。简单的…

web服务器攻击的八种方式

随着互联网的高速发展&#xff0c;网络走进了千家万户&#xff0c;同时也有很大一部分人架设起了自己的网站。继而不安分的黑客们&#xff0c;又将目光对准了服务器攻击这个方式&#xff0c;从而破坏或取得服务器的管理权限。本文将主要讲述针对web服务器攻击的八种方式。 1、…

Webpack--模块热替换(HMR)

一、概述 &#xff08;1&#xff09;live reload 只要检测到代码改动就会自动重新构建&#xff0c;然后触发网页刷新 &#xff08;2&#xff09;webapack中的模块热替换 可以让代码在页面不刷新的前提下得到最新的改动&#xff0c;甚至不需要重新发起请求就能看到更新后的效…

HMR API及其原理

很久之前&#xff0c;遇到一个面试题&#xff1a;【在代码变更之后&#xff0c;如何实时看到更新后的页面效果呢&#xff1f;】 在传统的方案中&#xff0c;我们可以通过 live reload 也就是自动刷新页面的方式来解决的&#xff0c;不过随着前端工程的日益庞大&#xff0c;开发…

webpack实践之路(七):模块热替换HMR

HMR 模块热替换(Hot Module Replacement 或 HMR)允许在运行时更新各种模块&#xff0c;而无需进行完全刷新。 HMR主要是通过以下几种方式&#xff0c;来显著加快开发速度&#xff1a; 保留在完全重新加载页面时丢失的应用程序状态。只更新变更内容&#xff0c;以节省宝贵的开…

【Webpack 性能优化系列(1) - HMR 热模块替换】

webpack系列文章&#xff1a; 【Webpack 性能优化系列(9) - 多进程打包】极大的提升项目打包构建速度&#xff01;&#xff01;&#xff01;【Webpack 性能优化系列(8) - PWA】使用渐进式网络应用程序为我们的项目添加离线体验【Webpack 性能优化系列(7) - 懒加载和预加载】【…

hmr webpack 不编译_Webpack HMR 热更新实现原理深入分析

概述 在使用 Webpack 构建开发期时&#xff0c;Webpack 提供热更新功能为开发带来良好的体验和开发效率&#xff0c;那热更新机制是怎么实现的呢&#xff1f; 代码实现 Webpack 配置添加 HotModuleReplacementPlugin 插件 new webpack.HotModuleReplacementPlugin({ // Options…