源码角度看待线程池的执行流程
文章目录
- 前言
- 一、线程池的相关接口和实现类
- 1.Executor接口
- 2.ExecutorService接口
- 3.AbstractExecutorService接口
- 4.ThreadPoolExecutor 实现类
- 二、ThreadPoolExecutor源码解析
- 1.Worker内部类
- 2.execute()方法
- 3.addWorker()方法
- 总结
前言
线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待空闲状态。如果有新的线程任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,线程池会创建一个新线程进行处理或者放入队列(工作队列)中等待。
线程池在多线程编程中扮演着重要角色,它能够管理和复用线程,提高并发执行效率。
在之前的学习中,我们知道了线程池的基本流程如下,而今天我们则用这个流程配合着源码的来重新分析线程池的执行流程。
一、线程池的相关接口和实现类
1.Executor接口
public interface Executor {/*** Executes the given command at some time in the future. The command* may execute in a new thread, in a pooled thread, or in the calling* thread, at the discretion of the {@code Executor} implementation.** @param command the runnable task* @throws RejectedExecutionException if this task cannot be* accepted for execution* @throws NullPointerException if command is null*/void execute(Runnable command);
}
Executor接口作为线程池技术中的顶层接口,它的作用是用来定义线程池中,用于提交并执行线程任务的核心方法:exuecte()方法。未来线程池中所有的线程任务,都将由exuecte()方法来执行。
2.ExecutorService接口
public interface ExecutorService extends Executor {//.....
}
ExecutorService接口继承了Executor接口,扩展了awaitTermination()、submit()、shutdown()等专门用于管理线程任务的方法。
3.AbstractExecutorService接口
public abstract class AbstractExecutorService implements ExecutorService {//....
}
ExecutorService接口的抽象实现类AbstractExecutorService,为不同的线程池实现类,提供submit()、invokeAll()等部分方法的公共实现。但是由于在不同线程池中的核心方法exuecte()执行策略不同,所以在AbstractExecutorService并未提供该方法的具体实现。
4.ThreadPoolExecutor 实现类
public class ThreadPoolExecutor extends AbstractExecutorService {//...
}
ThreadPoolExecutor实现类是AbstractExecutorService接口的的两个重要实现类之一,(ForkJoinPool是另一个),也是要掌握的关于线程池的重点区域;
ThreadPoolExecutor线程池通过Woker工作线程、BlockingQueue阻塞工作队列 以及 拒绝策略实现了一个标准的线程池;
二、ThreadPoolExecutor源码解析
在对源码进行解析之前,我们先看看官方给我们关于ThreadPoolExecutor的解析是什么:
The main pool control state, ctl, is an atomic integer packing
* two conceptual fields
* workerCount, indicating the effective number of threads
* runState, indicating whether running, shutting down etc
* …
这句话什么意思呢?
主池控制状态ctl是一个原子整数封装?
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
再结合ctl的实例化,我们这个发现这个ctl是一个具有原子性的整数,再往后就是告诉我们这个具有原子性的整数是由两个概念组成的:workerCount工作线程数和runState运行状态。
他是一个32位的整数,具体表示形式位:
所以说,它的作用就是通过位运算来存储线程池的状态和活动线程数信息。
1.Worker内部类
每个Woker类的对象,都代表线程池中的一个工作线程。
Worker类是ThreadPoolExecutor类中定义的一个私有内部类,保存了每个Worker工作线程要执行的Runnable线程任务和Thread线程对象。
private final class Worker extends AbstractQueuedSynchronizer implements Runnable { private static final long serialVersionUID = 6138294804551838833L;//用于存储当前工作线程的引用。final Thread thread;//用于存储该工作线程要执行的第一个任务。Runnable firstTask;//用于记录该工作线程已经完成的任务数量volatile long completedTasks;//初始化工作线程的状态,设置第一个任务并创建一个线程对象Worker(Runnable firstTask) {setState(-1); // inhibit interrupts until runWorkerthis.firstTask = firstTask;this.thread = getThreadFactory().newThread(this);}//Runnable 接口的 run 方法,调用了 runWorker(this),将工作线程自身作为参数传递给 runWorker方法public void run() {runWorker(this);}//因为继承了AbstractQueuedSynchronizer 类,一下方法都是基于线程安全的方法//..... }
当ThreadPoolExecutor线程池,通过exeute()方法执行1个线程任务时,会调用addWorker()方法创建一个Woker工作线程对象。并且,创建好的Worker工作线程对象,会被添加到一个HashSet workders工作线程集合,统一由线程池进行管理。
当Worker工作线程,在第一次执行完成线程任务后,这个Worker工作线程并不会销毁,而是会以循环的方式,通过线程池的getTask()方法,获取阻塞工作队列中新的Runnable线程任务,并通过当前Worker工作线程中所绑定Thread线程,完成新线程任务的执行,从而实现了线程池的中Thread线程的重复使用。
2.execute()方法
ThreadPoolExecutor线程池中,会通过execute(Runnable command)方法执行Runnable类型的线程任务。
在分析execute()方法之前,也来看看官方是怎么解释这个方法的:
/** Proceed in 3 steps:** 1. If fewer than corePoolSize threads are running, try to* start a new thread with the given command as its first* task. The call to addWorker atomically checks runState and* workerCount, and so prevents false alarms that would add* threads when it shouldn't, by returning false.** 2. If a task can be successfully queued, then we still need* to double-check whether we should have added a thread* (because existing ones died since last checking) or that* the pool shut down since entry into this method. So we* recheck state and if necessary roll back the enqueuing if* stopped, or start a new thread if there are none.** 3. If we cannot queue task, then we try to add a new* thread. If it fails, we know we are shut down or saturated* and so reject the task.*/
简单来说就是将execute()方法分成三个步骤:
1.如果,工作线程的数量小于核心线程数,则通过addWorker()方法,创建新的Worker工作线程,并添加至workers工作线程集合;
2.如果一个任务可以成功排队(工作线程的数量大于核心线程数),并且线程池处于RUNNING状态,那么,线程池会将Runnable类型的线程任务,缓存至workQueue阻塞工作队列,等待某个空闲工作线程获取并执行该任务;
3.如果我们不能排队任务,那么我们尝试添加一个新的线程。如果它失败了,我们知道我们被关闭或饱和了,因此拒绝该任务。
源码:
/* *@param 要提交给线程池的任务
*/
public void execute(Runnable command) {//如果传入的任务为空,抛出空指针异常。if (command == null)throw new NullPointerException();//获取当前线程池的状态和活动线程数。int c = ctl.get();//如果当前线程数小于核心线程数if (workerCountOf(c) < corePoolSize) {//如果可以通过addWorker方法创建一个新的工作线程来执行任务//因为当前线程数小于核心线程数,所以第二个参数穿入true代表创建的是核心线程if (addWorker(command, true))return;//创建成功,直接返回。//创建失败,获取更新后的线程池状态c = ctl.get();}//如果线程池状态是运行中且工作队列能够接受新任务if (isRunning(c) && workQueue.offer(command)) {//任务进入工作队列//重新获取线程池的状态和工作线程数int recheck = ctl.get();//如果线程池不是运行状态,则删除任务if (! isRunning(recheck) && remove(command))、//执行拒绝策略reject(command);// 如果工作线程数等于零,通过addWorker()方法检查线程池状态和工作队列else if (workerCountOf(recheck) == 0)addWorker(null, false);}//如果无法将任务添加到队列中,也无法创建新的工作线程,那么拒绝任务的执行else if (!addWorker(command, false))reject(command);}
3.addWorker()方法
在分析execute()方法中,发现execute()方法多次调用了addWorker()创建一个工作线程,用于执行当前线程任务。
addWorker()可以分为两个执行部分,检查线程池的状态和工作线程数量和创建并执行工作线程。
第1部分:检查线程池的状态和工作线程数量
//参数:1.传入的任务,2.是否创建核心线程
private boolean addWorker(Runnable firstTask, boolean core) {// 循环检查线程池的状态,直到符合创建工作线程的条件,通过retry标签break退出retry:for (;;) {//获取线程池运行状态int c = ctl.get();int rs = runStateOf(c);//如果线程池处于开始关闭的状态(获取线程任务为空,同时工作队列不等于空)if (rs >= SHUTDOWN &&! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))return false;//检查工作线程数量for (;;) {//获取当前工作线程数int wc = workerCountOf(c);//如果工作线程数量如果超出线程池的最大容量或者核心线程数(最大线程数)//三元运算符表示的是当前要的是核心线程还是非核心线程if (wc >= CAPACITY ||wc >= (core ? corePoolSize : maximumPoolSize))return false;//不再创建新的线程//通过ctl对象,将当前工作线程数量+1,并通过retry标签break退出外层循环if (compareAndIncrementWorkerCount(c))break retry;//再次获取线程池状态,检查是否发生变化c = ctl.get(); if (runStateOf(c) != rs)continue retry;//...}}}
第二部分:创建并执行线程工程
//....//用于判断工作线程是否启动和保存boolean workerStarted = false;boolean workerAdded = false;Worker w = null;try {//创建新工作线程,并通过线程工厂创建Thread线程w = new Worker(firstTask);//获取新工作线程的Thread线程对象,用于启动真正的线程final Thread t = w.thread; if (t != null) {//获取线程池的reentrantLock主锁,保证线程安全final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {//检查线程池运行状态int rs = runStateOf(ctl.get());//如果线程池状态小于关闭状态或者线程池状态为关闭且没有初始任务if (rs < SHUTDOWN ||(rs == SHUTDOWN && firstTask == null)) {//如果工作线程已经在运行(存活),抛出非法线程状态异常。if (t.isAlive()) // precheck that t is startablethrow new IllegalThreadStateException();//保存工作线程workers.add(w);//记录线程池的最大工作线程数int s = workers.size();if (s > largestPoolSize)largestPoolSize = s;workerAdded = true;}} finally {mainLock.unlock();}//正式启动线程if (workerAdded) {t.start();workerStarted = true;}}} finally {//如果工作线程没有成功启动,则调用添加失败的方法if (! workerStarted)addWorkerFailed(w);}//返回线程启动状态return workerStarted;
总结
execute()方法是ThreadPoolExecutor线程池执行的开始,它完整实现了Executor接口定义execute()方法,这个方法作用是执行一个Runnable类型的线程任务。整体的执行流程是:
相关文章:
源码角度看待线程池的执行流程
文章目录 前言一、线程池的相关接口和实现类1.Executor接口2.ExecutorService接口3.AbstractExecutorService接口4.ThreadPoolExecutor 实现类 二、ThreadPoolExecutor源码解析1.Worker内部类2.execute()方法3.addWorker()方法 总结 前言 线程池内部维护了若干个线程ÿ…...
我们的第一个 Qt 窗口程序
Qt 入门实战教程(目录) Windows Qt 5.12.10下载与安装 为何使用Qt Creator开发QT 本文介绍用Qt自带的集成开发工具Qt Creator创建Qt默认的窗口程序。 本文不需要你另外安装Visual Studio 2022这样的集成开发环境,也不需要你再在Visual St…...
Linux 8 下的容器引擎Podman概述
一、前言 最近在进行OS国产化交流中,了解到部分业务迁移到BClinux 8.2或Anolis 8.2时,原有docker业务需要迁移到新的容器平台:Podman,来完成容器的新的管理。Podman(全称 Pod Manager)是一款用于在 Linux 系…...
C++编辑修改PDF
PDFWriter是一个易于使用的C创建、修改PDF文档的库 1.创建一个PDF文件 #include #include “PDFWriter.h” int main() { std::cout << “Hello World!\n”; PDFWriter pdfWriter; int retpdfWriter.StartPDF(“D:\mytestwriterpdf.pdf”, ePDFVersion13); if (ret eS…...
数据倾斜优化
数据倾斜发生的原因有哪些? map输出数据按key Hash的分配到reduce中,由于key分布不均匀、业务数据本身的特性、建表时考虑不周等原因造成的reduce 上的数据量差异过大。 数据倾斜解决方式有哪些 group by 导致的数据倾斜 1.开启Map-Side聚合后&#x…...
Acwing796.子矩阵的和
理解二维前缀和: #include <iostream>using namespace std;const int N 1010;int a[N][N], s[N][N];int main() {int n, m, q;cin >> n >> m >> q;for (int i 1; i < n; i)for (int j 1; j < m; j) {scanf("%d", &a…...
【ELK日志收集系统】
目录 一、概述 1.作用 2.为什么使用? 二、组件 1.elasticsearch 1.1 作用 1.2 特点 2.logstash 2.1 作用 2.2 工作过程 2.3 INPUT 2.4 FILETER 2.5 OUTPUTS 3.kibana 三、架构类型 1.ELK 2.ELKK 3.ELFK 4.ELFKK 四、案例 - 构建ELK集群 1.环境…...
Java项目中实现信号的连续接收
系列文章目录 文章目录 系列文章目录前言一、监听信号二、信号处理逻辑三、停止信号监听总结 前言 在Java项目中,信号的连续接收是一项重要的任务,特别是在处理异步事件或者需要对外部事件做出响应时。本篇博客将介绍如何在Java项目中实现信号的连续接收…...
vue权限管理——按钮控制
1.按钮根据后端返回数据决定展示与否 根据right中的数据对应增删改查按钮 const menuList [{id: 1, path:/uploadSpec,authName: "上传spec", icon: User, children:[], rights:[view,add,edit,delete]},{id: 2, path:/showSpec, authName: "Spec预览",…...
jvm的内存区域
JVM 内存分为线程私有区和线程共享区,其中方法区和堆是线程共享区,虚拟机栈、本地方法栈和程序计数器是线程隔离的数据区。 1)程序计数器 程序计数器(Program Counter Register)也被称为 PC 寄存器,是一块…...
即时通讯开发中的性能优化技巧
即时通讯开发在如今的数字化社会中扮演着重要角色,然而,随着用户对即时通讯应用的需求不断增长,开发者们面临着使其应用保持高性能和可靠性的挑战。本文将探讨即时通讯开发中关键的性能优化技巧,帮助开发者们提升应用的用户体验和…...
flinkcdc同步完全量数据就不同步增量数据了
flinkcdc同步完全量数据就不同步增量数据了 使用flinkcdc同步mysql数据,使用的是全量采集模型 startupOptions(StartupOptions.earliest()) 全量阶段同步完成之后,发现并不开始同步增量数据,原因有以下两个: 原因1: …...
VBA:Application.GetOpenFilename打开指定文件夹里的excel类型文件(xls、xlsx)
GetOpenFilename相当于Excel打开窗口,通过该窗口选择要打开的文件,并可以返回选择的文件完整路径和文件名。 Application.GetOpenFilename(“文件类型筛选规则(就是说明)”,“优先显示第几个类型的文件”,“标题”,“是否允许选择多个文件名”) 打开类型…...
利用R作圆环条形图
从理念上看,本质就是增加了圆环弧度的条形图。如上图2。 需要以下步骤: 数据处理,将EXCEL中的数据做成3*N的表格导入系统,代码如下:library(tidyverse) library(stringr)library(ggplot2)library(viridis) stuper &…...
JavaScript(笔记)
目录 Hello World JavaScript 的变量 JavaScript 动态类型 隐式类型转换 JavaScript 数组 JavaScript 函数 JavaScript 中变量的作用域 对象 DOM 选中页面元素 事件 获取 / 修改元素内容 获取 / 修改元素属性 获取 / 修改 表单元素属性 获取 / 修改样式属性 新…...
软件工程(九) UML顺序-活动-状态-通信图
顺序图和后面的一些图,要求没有用例图和类图那么高,但仍然是比较重要的,我们也需要按程度去了解。 1、顺序图 顺序图(sequence diagram, 顺序图),顺序图是一种交互图(interaction diagram),它强调的是对象之间消息发送的顺序,同时显示对象之间的交互。 下面以一个简…...
JVM 是怎么设计来保证new对象的线程安全
1、采用 CAS 分配重试的方式来保证更新操作的原子性 2、每个线程在 Java 堆中预先分配一小块内存,也就是本地线程分配缓冲(Thread Local AllocationBuffer,TLAB),要分配内存的线程,先在本地缓冲区中分配&a…...
【JavaEE基础学习打卡00】该专栏知识大纲在这里!
目录 前言一、为什么有该教程二、教程内容介绍1.JavaEE2.JDBC3.JSP编程4.JavaBean5.Servlet6.综合案例7.拦截器、过滤器 三、学习前置要求四、课程服务总结 前言 📜 本系列教程适用于 Java Web 初学者、爱好者,小白白。我们的天赋并不高,可贵…...
C# 跨线程访问窗体控件
在不加任何修饰的情况下,C# 默认不允许跨线程访问控件,实际在项目开发过程中,经常使用跨线程操作控件属性,需要设置相关属性才能正确使用,两种方法设置如下: 方法1:告诉编译器取消跨线程访问检…...
Ctenos7安装mysql-8.1.0/tomcat-9.0.80/LNMT部署
目录 一、实验拓扑 二、部署mysql 三、部署Tomcat 四、配置NGINX 五、 配置NGINX的双机热备提高可用性 一、实验拓扑 二、部署mysql 官网下载地址https://dev.mysql.com/downloads/mysql/ 1、移除mariadb,安装所需应用 mysql-8.1.0 社区版 安装说明官网下载地址…...
input时间表单默认样式修改(input[type=“date“])
一、时间选择的种类: HTML代码: <input type"date" value"2018-11-15" />选择日期: 选择时间: <input type"time" value"22:52" />在这里插入图片描述 选择星期: <…...
首页搜索框传递参数,并在搜索页面中的搜索框中进行显示,搜索框绑定回车键进行搜索
实现搜索条件和搜索内容固定,以及回车键搜索跳转 1.写出搜索条件和搜索框 <form class"parent"><select id"searchSelect" style"border: 1px solid #325da7;border-right: none;" value"resource"><opt…...
【Vue3+Ts】项目启动准备和配置项目代码规范和css样式的重置
项目启动准备 创建项目( 使用Vite 构建工具创建项目模板)目录介绍插件安装创建别名编译说明项目配置配置icon和标题配置项目别名配置ts.config.json检测vscode的插件是否配置 配置项目代码规范集成editorconfig配置prettier工具库ESLint检测配置 CSS样式…...
Java【手撕双指针】LeetCode 15. “三数之和“, 图文详解思路分析 + 代码
文章目录 前言一、三数之和1, 题目2, 思路分析3, 代码 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: 📕 JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等 📗 Java数据结构: 顺序表, 链表, 堆…...
Flutter:自定义组件的上下左右弹出层
背景 最近要使用Flutter实现一个下拉菜单,需求就是,在当前组件下点击,其下方弹出一个菜单选项,如下图所示: 实现起来,貌似没什么障碍,在Flutter中本身就提供了弹出层PopupMenuButton组件和show…...
C++处理终端程序中断或意外退出的情况
目录 背景和需求解决方法关于信号类型 背景和需求 Linux环境中,有一个可执行程序,假设该程序的运行生命周期需要调用下面四个函数: int connect(); int start();int end(); int disconnect();如果用户在程序运行期间,手动CTRLC或…...
分布式锁:业务锁和定时任务锁
一:业务锁 在代码业务逻辑加锁,防止不同业务操作相同业务表导致数据错乱,设置锁进行等待。这里锁使用的是ReentrantLock。详细的介绍可以参考: https://blog.csdn.net/jerry11112/article/details/112375167 Slf4j public class…...
路由器的简单概述(详细理解+实例精讲)
系列文章目录 华为数通学习(4) 目录 系列文章目录 华为数通学习(4) 前言 一,网段间通信 二,路由器的基本特点 三,路由信息介绍 四,路由表 五,路由表的来源有哪些…...
Mapper.xml文件解析
Mapper.xml文件解析 简单解读 最近在做一个分布式项目,看到xml文件原先只是上网CV,还是要搞清楚吧! 下面是一个Mybatis的SQL映射文件的配置 <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE mapper PUBLIC…...
ES 7.6 - JAVA应用基础操作篇
ES 7.6 - JAVA应用基础操作篇 环境准备依赖配置 实体类准备使用说明索引/映射操作创建索引和映射索引和映射相关查询删除索引 文档操作插入数据更新数据删除数据批量操作 文档查询根据ID查询根据字段精准查询根据字段分词查询控制返回字段范围查询组合查询排序分页高亮搜索聚合…...
com.squareup.okhttp3:okhttp 组件安全漏洞及健康度分析
组件简介 维护者square组织许可证类型Apache License 2.0首次发布2016 年 1 月 2 日最新发布时间2023 年 4 月 23 日GitHub Star44403GitHub Fork9197依赖包5,582依赖存储库77,217 com.squareup.okhttp3:okhttp 一个开源的 HTTP 客户端库,可以用于 Android 和 Jav…...
【Unity的HDRP渲染管线下用Steam VR串流结合使用遇到的各种问题_SteamVR 插件和Pico串流助手】
用Steam串流VR 背景:1.项目准备:相关文档和社区资源需要下载的工具2.梳理工程渲染设置和场景烘培正确:几个概念的一些说明:1. SteamVR:2. SteamVR插件:3. OpenVR和OpenXR:4. XRI:5. Pico串流助手:6. "Mock Runtime"选项含义SteamVR插件导入配置好SteamVR Came…...
Unity——音乐、音效
在游戏运行的过程中,音效的播放时机与游戏当前内容密切相关,而且随着场景的变化、剧情的推进,背景音乐也需要适时切换,所以恰当地控制音乐和音效的播放非常重要。音乐和音效的播放、停止、切换和音量变化等,都需要由脚…...
Ubuntu 23.10 将首次推出基于 Flutter 的新 Ubuntu 商店
导读Ubuntu 正在升级其软件商店以提供顺滑的体验! 随着不断发展,Canonical 似乎全力以赴,将基于 Flutter 的元素整合到 Ubuntu 中。 在前段时间 Ubuntu 23.04 发布后,我们见到了基于 Flutter 的安装程序 ,现在&#x…...
linux scatterlist阅读三
sg_copy_buffer 函数定义: /*** sg_copy_buffer - Copy data between a linear buffer and an SG list* sgl: The SG list* nents: Number of SG entries* buf: Where to copy from* buflen: The number of bytes to copy* skip: Number of bytes to sk…...
2023新,centos7安装mysql8.0.25
2023新,centos7安装mysql8.0.25 目录 2023新,centos7安装mysql8.0.251、下载rpm文件2、安装3、配置my.cnf4、启动查看重启服务5、登入mysql并修改密码6、修改可以远程登录 1、下载rpm文件 进入到你想要的文件地址下 wget https://repo.mysql.com//mysq…...
Data Rescue Professional for Mac:专业的数据恢复工具
在数字化时代,我们的生活和工作离不开电脑和存储设备。但是,意外情况时常发生,例如误删除文件、格式化硬盘、病毒攻击等,这些都可能导致重要的数据丢失。面对数据丢失,我们迫切需要一款可靠的数据恢复工具。今天&#…...
新手小白想要做好跨境电商独立站,需要考虑哪些要素?
对于不少中小卖家而言,利用独立站出海已然成为下一个跨境热潮。但是采用独立站模式做出海生意前,卖家需要考虑哪些要素? 产品选择 对于国内的卖家来说,依托于国内强大的供应链优势,只要能把握住消费者心态࿰…...
Consul原理介绍
官方文档:https://www.consul.io/docs Raft动画演示:http://thesecretlivesofdata.com/raft/ 注册中心对比 Consul特点 服务发现、健康检查、Key/Value存储、安全服务通信(TLS证书)、多数据中心 架构 角色 数据中心 数据中心内…...
【C++实战】C++实现贪吃蛇(含源代码)—基于easyx图形库
食用指南:本文在有C基础的情况下食用更佳 🍀本文前置知识:C基础 ♈️今日夜电波:toge—あよ 0:36 ━━━━━━️💟──────── 4:03 &a…...
PHP获取两个日期之间的所有日期
下面是一个示例代码,用于计算给定开始和结束日期之间的所有日期: <?phpfunction getDatesBetween($start_date, $end_date) {// 初始化结果数组$dates array();// 将开始日期转换为时间戳$current_date strtotime($start_date);$end_date strtot…...
STL之stack(适配器讲解以及双端队列的讲解)
很多人在听到适配器的时候,应该都是懵的,因为对适配器的理解都是懵懵懂懂,其实他很好理解,就是相当于一个转换器。我们可以这样理解,就是现实当中是的插排一样,上面有三个孔的,也有两个孔的&…...
JVM解密: 解构类加载与GC垃圾回收机制
文章目录 一. JVM内存划分二. 类加载机制1. 类加载过程2. 双亲委派模型 三. GC垃圾回收机制1. 找到需要回收的内存1.1 哪些内存需要回收?1.2 基于引用计数找垃圾(Java不采取该方案)1.3 基于可达性分析找垃圾(Java采取方案) 2. 垃圾回收算法2.1 标记-清除算法2.2 标记…...
【Spring Boot】Spring Boot结合MyBatis简单实现学生信息管理模块
实战:实现学生信息管理模块 环境准备 JDKSpring BootMyBatis 创建Spring Boot项目 使用Spring Initializr创建一个新的Spring Boot项目,并添加以下依赖: Spring WebMyBatis FrameworkMySQL Driver 数据库设计 在MySQL数据库中创建一个名…...
【Java List与Map】List<T> Map与Map List<T>的区别(126)
List<T> Map:List里面的数据类型包含Map; Map List<T>:Map里面value的数据类型包含List; 测试案例: import java.util.ArrayList; import java.util.HashMap; import j…...
【FreeRTOS】常用函数总结
xTaskCreate(): 用法: xTaskCreate(taskFunction, taskName, stackSize, parameters, priority, taskHandle)参数: taskFunction:任务函数,即任务的入口函数。taskName:任务的名称。stackSize:任…...
The Cherno——OpenGL
The Cherno——OpenGL 1. 欢迎来到OpenGL OpenGL是一种跨平台的图形接口(API),就是一大堆我们能够调用的函数去做一些与图像相关的事情。特殊的是,OpenGL允许我们访问GPU(Graphics Processing Unit 图像处理单元&…...
linux中学习控制进程的要点
1. 进程创建 1.1 fork函数 #include <unistd.h> pid_t fork(void); 返回值:自进程中返回0,父进程返回子进程id,出错返回-1 进程调用fork,当控制转移到内核中的fork代码后,内核会做以下操作 分配新的内存块和…...
C++Qt QSS要注意的坑
qss源自css,相当于css的一个子集,主要支持的是css2标准,很多网上的css3的标准的写法在qss这里是不生效的,所以不要大惊小怪。 qss也不是完全支持所有的css2,比如text-align官方文档就有说明,只支持 QPushB…...
LeetCode每日一题:56. 合并区间(2023.8.27 C++)
目录 56. 合并区间 题目描述: 实现代码与解析: 排序 贪心 原理思路: 56. 合并区间 题目描述: 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&…...