【Java多线程】线程池探究
创始人
2025-06-01 01:49:02
0

什么是线程池

线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。

线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。

本文学习记录的线程池是JDK中提供的ThreadPoolExecutor类。

那些地方用到线程池

实际开发中项目中,禁止自己new线程。必须使用线程池来维护和创建线程。

线程池的作用

核心:复用机制。提前创建好固定的线程一直在运行状态,实现复用,限制线程创建数量。

使用线程池可以带来一系列好处:

  • 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
  • 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
  • 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
  • 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

线程池的创建

在这里插入图片描述

例子:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Threadpool {public static void main(String[] args) {ExecutorService executorService = Executors.newCachedThreadPool();for (int i = 0; i < 10; i++) {final int finalI = i;executorService.execute((new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+","+finalI);}}));}}
}//输出:
pool-1-thread-2,1
pool-1-thread-6,5
pool-1-thread-5,4
pool-1-thread-4,3
pool-1-thread-3,2
pool-1-thread-8,7
pool-1-thread-1,0
pool-1-thread-7,6
pool-1-thread-9,8
pool-1-thread-10,9
//上面程序发现没有复用,查看源码发现可以创建MAX_VALUE的线程,不能体现特点。
//改用Executors.newFixedThreadPool(),可以解决

ThreadPoolExecutor核心参数有哪些

corePoolSize:核心线程数量 一直保持运行的线程

​ 线程池中的核心线程数,默认情况下核心线程一直存活在线程池中,如果将ThreadPoolExecutor 的allowCoreThreadTimeOut 属性设为true,如果线程池一直闲置并超过了keepAliveTime 所指定的时间,核心线程就会被终止。

maximumPoolSize:最大线程数,线程池允许创建的最大线程数

keepAliveTime:超出corePoolSize后创建的线程的存活时间。

unit:keepAliveTime时间单位

workQueue:任务队列,用于保存待执行的任务。

threadFactory:线程池内部创建线程所用的工厂。

handler:任务无法执行时的处理器。

线程池底层原理

在这里插入图片描述

线程池任务调度

在这里插入图片描述

所有任务的调度都是由execute方法完成的,这部分完成的工作是:检查现在线程池的运行状态、运行线程数、运行策略,决定接下来执行的流程,是直接申请线程执行,或是缓冲到队列中执行,亦或是直接拒绝该任务。其执行过程如下:

  1. 首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
  2. 如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
  3. 如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
  4. 如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
  5. 如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。

线程池创建的线程会一直在运行状态吗

不会。例如:配置核心线程数corePoolSize为2、最大线程数maximumPoolSize为5,我们可以通过配置超出corePoolSize核心线程数后创建的线程存活时间例如60s。在60s内没有核心线程一直没有任务执行,则会停止该线程。

为什么阿里巴巴不建议使用Executors

因为默认的Executors线程池底层是基于ThreadPoolExecutor构造函数封装的,采用无界队列存放缓存任务,会一直缓存任务容易发生内存溢出,会导致我们最大线程数失效。

ThreadPoolExecutor底层实现原理

手写线程池

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;public class MyExecutors {private List workThreads;private BlockingDeque runnableDeque;private boolean isRun = true;/*最大线程数@param maxThreadCount*/public MyExecutors(int maxThreadCount, int dequeSize){//1.限制队列容量缓存runnableDeque = new LinkedBlockingDeque(dequeSize);//2.提前创建好固定的的线程一直在运行状态---死循环实现new ArrayList(maxThreadCount);for (int i = 0; i < maxThreadCount; i++) {new WorkThread().start();}}class WorkThread extends Thread{public void run(){while (isRun || runnableDeque.size()>0){Runnable runnable = runnableDeque.poll();if (runnable != null){runnable.run();}}}}public boolean execute(Runnable command){return runnableDeque.offer(command);}public static void main(String[] args) {MyExecutors myExecutors = new MyExecutors(2,2);for (int i = 0; i < 10; i++) {final int finalI = i;myExecutors.execute(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"," + finalI);}});}myExecutors.isRun = false;}
}

实际最多执行多少个任务 核心线程数+缓存队列的容量+最大线程数-核心线程数

线程池拒绝策略类型有哪些

1.AbortPolicy 丢弃任务,抛运行时异常

2.CallerRunsPolicy 执行任务

3.DiscardPolicy 忽视,什么都不会发生

4.DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务

5.实现 RejectedExecutionHandler 接口,可自定义处理器

线程池状态和生命周期

在这里插入图片描述

参考资料

全面学习可参考:

Java线程池实现原理及其在美团业务中的实践

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
JAVA多线程知识整理 Java多线程基础 线程的创建和启动 继承Thread类来创建并启动 自定义Thread类的子类&#...
【洛谷 P1090】[NOIP... [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G ...
国民技术LPUART介绍 低功耗通用异步接收器(LPUART) 简介 低功耗通用异步收发器...
城乡供水一体化平台-助力乡村振... 城乡供水一体化管理系统建设方案 城乡供水一体化管理系统是运用云计算、大数据等信息化手段࿰...
程序的循环结构和random库...   第三个参数就是步长     引入文件时记得指明字符格式,否则读入不了 ...
中国版ChatGPT在哪些方面... 目录 一、中国巨大的市场需求 二、中国企业加速创新 三、中国的人工智能发展 四、企业愿景的推进 五、...
报名开启 | 共赴一场 Flu... 2023 年 1 月 25 日,Flutter Forward 大会在肯尼亚首都内罗毕...
汇编00-MASM 和 Vis... Qt源码解析 索引 汇编逆向--- MASM 和 Visual Studio入门 前提知识ÿ...
【简陋Web应用3】实现人脸比... 文章目录🍉 前情提要🌷 效果演示🥝 实现过程1. u...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
windows安装JDK步骤 一、 下载JDK安装包 下载地址:https://www.oracle.com/jav...
分治法实现合并排序(归并排序)... 🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨...
在linux上安装配置node... 目录前言1,关于nodejs2,配置环境变量3,总结 前言...
Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
Linux内核进程管理并发同步... 并发同步并发 是指在某一时间段内能够处理多个任务的能力,而 并行 是指同一时间能够处理...
opencv学习-HOG LO... 目录1. HOG(Histogram of Oriented Gradients,方向梯度直方图)1...
EEG微状态的功能意义 导读大脑的瞬时全局功能状态反映在其电场结构上。聚类分析方法一致地提取了四种头表面脑电场结构ÿ...
【Unity 手写PBR】Bu... 写在前面 前期积累: GAMES101作业7提高-实现微表面模型你需要了解的知识 【技...