当前位置: 首页 > news >正文

Linux操作系统学习(线程基础)

文章目录

  • 线程的基础概念
    • 线程控制
    • 内核LWP和线程ID的关系

线程的基础概念

​ 一般教材对线程描述是:是在进程内部运行的一个分支(执行流),属于进程的一部分,粒度要比进程更加细和轻量化

​ 一个进程中是可能存在多个线程的,可能是1:1也可能是1:n,所以线程也应由OS管理,管理就离不开“先描述,再组织”,所以线程也应该类似PCB一样有线程控制块TCB,但Linux中没有专门为线程设计的TCB,而是用进程PCB来模拟线程

​ 通过某些函数方法,可以在同一个进程中只创建task_struct,让这些PCB共享同一个地址空间,把当前进程的资源(代码+数据)划分成若干份,让每个PCB使用执行一部分。

​ 站在CPU的角度,CPU只看PCB,不关心是否共享一份地址空间,此时CPU看到的PCB就是一个需要被调度的执行流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ovbkmZxa-1677869543495)(G:\Typora\图片保存\image-20221222011520050.png)]

​ Linux中是用进程来模拟线程的,所以也叫轻量级进程(站在用户角度是线程),不用单独为线程设计算法,可直接用进程的一套相关方法,所以不用维护复杂的进程和线程的关系。OS只需要聚焦在线程间的资源分配上就可以了,所以CPU是只看PCB,不区分线程和进程。

进程与线程的区别:

  • 进程具有独立性,可以有部分资源共享(基于管道、ipc资源下)

  • 线程共享进程的数据,但是也有属于自己的数据

  • 进程是资源分配的基本单位,承担系统资源分配的基本实体

  • 线程是CPU调度的基本单位,承担进程资源一部分的基本实体

线程的优点:

  • 创建一个新的线程的代价比创建一个进程小得多。创建一个线程虽然也需要创建数据结构,但是并不需要重新开辟资源,只需要将进程的部分资源分配给线程。创建一个进程不仅需要创建大量数据结构,还需要重新创建资源。

  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作少。线程只是进程的部分资源,切换的资源少。

  • 线程占用的资源比进程少

  • 能充分利用多处理器的可并行数量

  • 在等待慢速的I/O任务结束的同时,程序可以执行其它的计算任务

  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现。

  • I/O密集型应用,为了提高性能,将I/O操作重叠,线程可以同时等待不同的I/O操作。I/O操作是与外设交互数据,会很慢。

线程的缺点:

  • 性能缺失
    一个处理器只能处理一个线程,如果线程数比可用处理器数多,会有较大的性能损失,会增加额外的同步和CPU调度的开销,而资源却是不变的

  • 健壮性降低
    编写多线程时, 可能因为共享的变量, 导致一个线程修改此变量, 影响另外一个线程。(线程不是对立的,所以线程之间缺乏保护)

    ​ (多线程之间的变量是同一个变量;多进程之间变量不是同一个变量,一开始是共享父进程的,但在改变时发生写时拷贝)

  • 缺乏访问的控制
    进程时访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响

  • 编程难度相对高

线程异常:

  • 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃 ;线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,该进程内的所有线程也就随即退出

线程用途:

  • 合理的使用多线程,能提高CPU密集型程序的执行效率
  • 合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是 多线程运行的一种表现)

线程与进程的关系

在这里插入图片描述


线程控制

​ 因为是用进程模拟的线程,所以Linux下不会给我们提供直接操作线程的接口,而给线程提供的是在同一个地址空间创建PCB的方法、分配资源给指定的PCB的接口等等,需要用户自己创建一些函数方法如创建线程、释放线程、等待线程等,对用户不太友好。

​ 所以一般我们在使用的线程相关函数方法,都是由系统级别的工程师在用户层对Linux轻量级进程提供的方法进行封装打包成的库。

下面主要介绍pthread库

  1. 创建线程函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wsdkuadx-1677869543496)(G:\Typora\图片保存\image-20221223164100513.png)]

  • threaad:输出型参数,会获取刚创建的线程ID
  • attr:传入的是线程的属性(优先级、运行栈等等)一般传入NULL,交给OS默认处理。
  • start_routine:创建线程后所执行的方法
  • arg:一般会传给第三个参数的函数方法
#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>void* ThreadRun(void* args)
{std::string name = (char*)args;while(true){std::cout << "当前处于新线程,新线程ID是:" << pthread_self() << ", args: " << name << std::endl;sleep(1);}
}int main()
{pthread_t tid[5];for(int i = 0;i < 5;i++)pthread_create(&tid[i],nullptr,ThreadRun,(void*)"new thread");while(true){std::cout << "当前处于主线程,主线程ID是:" << pthread_self() << std::endl;std::cout << "###################################################################" << std::endl;for(size_t i = 0;i < 5;i++)std::cout << "创建的新线程[" << i << "]ID是:" << tid << std::endl;std::cout << "###################################################################" << std::endl;sleep(3);}return  0;
}

犹豫是多线程打印,会有乱码现象。

代码中用到的pthread_self(),作用是在谁的线程下用,就获取谁的线程 ID

在这里插入图片描述

​ 可以看到的是,他们的PID都是一样的,所以线程是对进程的资源分配 ,LWP是内核提供的,和打印出来的id是不一样的,这里用的是原生线程库,打出来的id是其实是进程地址号

  1. 线程等待

进程退出有三种方式

  • 代码正常运行,结果正确
  • 代码正常运行,结果不对
  • 程序异常,直接退出

​ 前两点退出时不会产生信号,但会产生退出码,而程序异常会产生终止信号;线程分配的资源是进程的一部分,所以只要有一个线程执行的代码部分导致程序崩溃,整个进程会直接退出并产生终止信号,整个进程崩溃,所有线程也就崩溃了。

​ 一般而言,线程也是需要等待的,否则也会出现类似僵尸进程那样的问题,已经退出的线程,其空间没有被释放,仍然在进程的地址空间内,占用资源还得维护数据

​ 线程等待只能是在代码正常运行的前提下等待,而程序异常,进程就会直接退出了,OS会向进程发送退出信号,此时与线程无关,下图所示线程等待函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g8lH0kwN-1677869543496)(G:\Typora\图片保存\image-20221223182409990.png)]

  • thread:需要等待的线程的ID

  • retval:输出型参数,获取创建线程时指定的函数方法的返回值,(因为指定的函数方法返回值是指针返回,所以这里是二级指针)

    pthread_join函数在等待时是阻塞式等待

#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>void* ThreadRun(void* args)
{sleep(3);return (void*)"finish";
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,ThreadRun,(void*)"new thread");void* status = NULL;pthread_join(tid,&status);std::cout << (char*)status << std::endl;return  0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jVsAbkzU-1677869543496)(G:\Typora\图片保存\image-20221223182123417.png)]

  1. 线程退出

    线程退出的方法有三种

  • 函数中return

    • main函数return代表主线程和进程退出

    • 其他线程函数中return,代表当前线程退出

  • 通过pthread_exit函数终止线程

!](https://img-blog.csdnimg.cn/24870b7ee8624fc297d3f4b109cc4ce6.png)

参数是要传入的退出状态

void* ThreadRun(void* args)
{std::string name = (char*)args;std::cout << name << " running. . ." << std::endl;sleep(3);pthread_exit((void*)123);std::cout << "exit fail" << std::endl;return (void*)"finish";
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,ThreadRun,(void*)"new thread");void* status = NULL;pthread_join(tid,&status);printf("%d\n",(int*)status);return  0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wFkoQuxU-1677869543497)(G:\Typora\图片保存\image-20221223194429944.png)]

  • 通过pthread_cancel函数取消线程

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FmTkx3LF-1677869543497)(G:\Typora\图片保存\image-20221223195240763.png)]

    只需要传入要取消的线程的id即可

    成功返回0,失败返回-1

    void* ThreadRun(void* args)
    {std::string name = (char*)args;std::cout << name << " running. . ." << std::endl;sleep(10);return (void*)"finish";
    }int main()
    {pthread_t tid;pthread_create(&tid,nullptr,ThreadRun,(void*)"new thread");sleep(1);std::cout << "cancel sub thread. . ." << std::endl;sleep(5);pthread_cancel(tid);void* status = NULL;pthread_join(tid,&status);printf("%d\n",(int*)status);return  0;
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LhJGzBKU-1677869543497)(G:\Typora\图片保存\image-20221223195949508.png)]

    取消后的线程退出码是 -1 代表是PTHREAD_ CANCELED的意思

注意:不建议在子线程中取消主线程的做法,这样会导致进入僵尸进程的状态。因为主线程会退出而没有资源可以等待子线程退出,子线程就造成资源浪费了

void* ThreadRun(void* args)
{sleep(5);pthread_cancel((pthread_t)args);while(1){std::cout << " running. . ." << std::endl;sleep(1);}return (void*)"finish";
}int main()
{pthread_t tid;pthread_t g_tid = pthread_self();pthread_create(&tid,nullptr,ThreadRun,(void*)g_tid);void* status = NULL;pthread_join(tid,&status);printf("%d\n",(int*)status);return  0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6sFL15Cs-1677869543498)(G:\Typora\图片保存\image-20221223203927069.png)]

如图所示:进入了僵尸进程,以及类似僵尸进程的线程状态(主线程 defunct)

  1. 线程分离

若不想阻塞式的等待线程退出,可以使用线程分离函数;分离之后的线程不需要被join,运行完毕后会自动释放资源

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WDoqmLHI-1677869543498)(G:\Typora\图片保存\image-20221223222753176.png)]

传入要分离的线程id即可

void* ThreadRun(void* args)
{pthread_detach(pthread_self());std::cout << " running. . ." << std::endl;sleep(1);return (void*)"finish";
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,ThreadRun,(void*)"hello");sleep(3);void* status = NULL;int ret = pthread_join(tid,&status);printf("ret:%d,status:%s\n",ret,(char*)status);return  0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X1F2IqD1-1677869543498)(G:\Typora\图片保存\image-20221223222813924.png)]

只要线程分离了,就不可在使用join去等待了,会等待失败

内核LWP和线程ID的关系

​ 我们使用的pthread原生线程库是存在磁盘当中的,在使用时会被加载到内存当中,通过页表映射到进程地址空间的共享区(堆区和栈区之间的区域)所有的进程都共用同一份库(只是进程空间中进程自己认为独占这个库)

​ 而每个线程共享的是同一份进程地址空间,但是每个线程也会有自己的私有数据部分如线程栈(主线程栈是在栈区的)、上下文数据、属性等等。这些线程私有的部分组成线程的数据块(可以想象成类似于task_struct的线程struct),数据块其实是存储在线程库中的,通过页表映射到进程地址的共享区。

(创建线程的时候,用mmap系统调用从heap分配出来的)

​ 在使用pthread_create创建线程时,线程库会生成一个ID号来表示新创建的线程,这个ID由函数的第一个参数可以获取到;而线程ID就是这些数据块在共享区映射的地址,每个线程id就是每个线程的数据块在共享区的地址,通过id号便能找到线程的所有数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z1FzwW8l-1677869543498)(G:\Typora\图片保存\image-20221224021321882.png)]

线程id就是:线程存储在线程库中的数据,通过内存映射到进程地址空的共享区的地址标号,有了这个标号就能找到线程的所有的私有数据

LWP和线程id的关系是什么?

LWP是在内核层面的,线程id是在用户层面的。

​ 前面说过在Linux中没有提供专门的线程方法,使用进程模拟的线程,所以Liunx中的线程其实是轻量级进程,依旧用的task_struct,所以CPU调度知认PCB;那进程由PID,线程也该有个数字来标识这些task_struct,那这个标识就是LWP,所以LWP实际上是供CPU调度使用的(类似文件描述符一样)

​ 线程id是用户层的,LWP是内核层的,那id怎么知道他是对应哪个task_struct的呢?所以线程的数据块一定也包含一个LWP,形成1:1的关系(也有1:n,即一个线程id包含多个LWP,在用户层面看来是一个线程)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lfYM5XQC-1677869543499)(G:\Typora\图片保存\image-20221224023454692.png)]

相关文章:

Linux操作系统学习(线程基础)

文章目录线程的基础概念线程控制内核LWP和线程ID的关系线程的基础概念 ​ 一般教材对线程描述是&#xff1a;是在进程内部运行的一个分支&#xff08;执行流&#xff09;&#xff0c;属于进程的一部分&#xff0c;粒度要比进程更加细和轻量化 ​ 一个进程中是可能存在多个线程…...

YOLOv5源码逐行超详细注释与解读(1)——项目目录结构解析

前言 前面简单介绍了YOLOv5的网络结构和创新点&#xff08;直通车&#xff1a;【YOLO系列】YOLOv5超详细解读&#xff08;网络详解&#xff09;&#xff09; 在接下来我们会进入到YOLOv5更深一步的学习&#xff0c;首先从源码解读开始。 因为我是纯小白&#xff0c;刚开始下…...

前端开发总结的一些技巧和实用方法(2)

本文主要介绍一些JS中用到的小技巧和实用方法&#xff0c;可以在日常Coding中提升幸福度&#xff0c;也可以通过一些小细节来增加代码可读性&#xff0c;让代码看起来更加优雅&#xff0c;后续将不断更新1.数组 map 的方法 (不使用Array.Map) Array.from 还可以接受第二个参数…...

Docker搭建jenkins(Vue自动化部署)

前言 需要提前准备的条件 Docker环境 一、jenkins镜像 # 查询镜像 docker search jenkins# 下载镜像 # lts稳定版 docker pull jenkins/jenkins:lts#查看镜像 docker images二、启动Jenkins容器 创建挂载文件夹&#xff0c;并且进行文件授予权限 #创建文件夹 mkdir -p /home/j…...

ADCS攻击之CVE-2022–26923

CSDN自动博客文章迁移漏洞简介该漏洞允许低权限用户在安装了 Active Directory 证书服务 (AD CS) 服务器角色的默认 Active Directory 环境中将权限提升到域管理员。在默认安装的ADCS里就启用了Machine模板。漏洞利用添加机器账户&#xff0c;并将该机器账户dnsHostName指向DC[…...

AO3401-ASEMI低压P沟道MOS管AO3401

编辑&#xff1a;ll AO3401-ASEMI低压P沟道MOS管AO3401 型号&#xff1a;AO3401 品牌&#xff1a;ASEMI 封装&#xff1a;SOT-23 最大漏源电流&#xff1a;-4.2A 漏源击穿电压&#xff1a;-30V RDS&#xff08;ON&#xff09;Max&#xff1a;0.05Ω 引脚数量&#xff1…...

【STM32MP157应用编程】3.控制PWM

目录 PWM文件 指令操作PWM 程序操作PWM 程序说明 程序代码 3_PWM_1.c 启动交叉编译工具 编译 拷贝到开发板 测试 PWM文件 在/sys/class/pwm目录下&#xff0c;存放了PWM的文件。 pwmchip0和pwmchip4目录对应了MP157 SoC的2个PWM控制器&#xff0c;pwmchip0对应的是M…...

基于Python的selenium

一、安装 1.1安装Python&#xff0c;安装Python时需要勾选增加环境变量 如果之前已经安装过Python&#xff0c;需要将Python相关文件以及环境变量删除 1.2安装成功&#xff1a;在命令行界面下输入Python&#xff0c;最终展示>>>即可成功 2.1安装pycharm,直接自定义安装…...

Go底层原理:一起来唠唠GMP调度(一)

目录前言一、进程、线程、Goroutine1、进程与线程2、Goroutine二、Go调度器设计思想1、线程模型1.1 内核级线程模型1.2 用户级线程模型1.3 混合型线程模型2、 被废弃的 G-M 调度器2.1 了解 G-M 调度如何工作3、如今高效的 GMP 模型3.1 GMP模型调度流程3.2 GMP调度设计策略3.3 G…...

前端——1.相关概念

这篇文章主要介绍前端入门的相关概念 1.网页 1.1什么是网页&#xff1f; 网站&#xff1a;是指在因特网上根据一定的规则&#xff0c;使用HTML等制作的用于展示特定内容相关的网页集合 网页&#xff1a;是网站中的一“页”&#xff0c;通常是HTML格式的文件&#xff0c;它要…...

java四种线程池(基本使用)

标题java四种线程池及使用示例 1、线程工厂 1、我们先来写ThreadFactory&#xff0c;在创建线程池时候可以传入自定义的线程工厂&#xff0c;线程工厂说白了就是用来定制线程的一些属性&#xff1a;名字、优先级、是否为守护线程。直接看代码即可。 当然创建线程池的时候可以…...

float的表示范围为什么比long大

●很多人会有一个疑问, 一个用来表示小数的 float 为什么表示的范围会比 long 还要大呢 ? ●这次, 咱们就来详细说一说这个事情 从长计议 ●聊到这个话题, 我们就要从计算机存储数字这个位置说起了 ●计算机存储数字的方式其实就是 : 二进制 二进制是计算机中最基本的数字存储…...

Flutter Android 打包保姆式全流程 2023 版

大家好&#xff0c;我是 17。 为什么要写这篇文章呢&#xff1f;对于一没有 android 开发经验&#xff0c;从未有过打包经历的新人来说&#xff0c;要想成功打包&#xff0c;是很困难的。因为受到的阻碍太多&#xff0c;是完全陌生的领域&#xff0c;几乎是寸步难行。如果有老…...

C++笔记之lambda表达式

引言 Lambda表达式是从C 11版本引入的特性&#xff0c;利用它可以很方便的定义匿名函数对象&#xff0c;通常作为回调函数来使用。大家会经常拿它和函数指针&#xff0c;函数符放在一起比较&#xff0c;很多场合下&#xff0c;它们三者都可以替换着用。 语法 [ captures ] (…...

flink大数据处理流式计算详解

flink大数据处理 文章目录flink大数据处理二、WebUI可视化界面&#xff08;测试用&#xff09;三、Flink部署3.1 JobManager3.2 TaskManager3.3 并行度的调整配置3.4 区分 TaskSolt和parallelism并行度配置四、Source Operator(资源算子)五、Sink Operator(输出算子)六、Flink滑…...

Java面试题(二十三)DCL单例

懒汉式单例 private static SingletonInstance INSTANCE;private SingletonInstance(){}public static SingletonInstance getInstance() {if (INSTANCE null) {INSTANCE new SingletonInstance();}return INSTANCE;}构造方法私有化&#xff0c;然后判断是否为空&#xff0c;…...

UML-类图

一、类 一个类由三个格子组成&#xff0c;从上至下分别表示&#xff1a; 第一格&#xff1a;类名称&#xff08;接口和抽象类&#xff0c;使用斜体&#xff09; 第二格&#xff1a;类的属性&#xff08;成员变量&#xff0c;可以没有&#xff09; 第三格&#xff1a;类的操作&…...

PostgreSQL 数据库和 pgAdmin 4

PostgreSQL 数据库和 pgAdmin 4PostgreSQLPostgreSQL 数据库安装PostgreSQL 数据库安装 (Ubuntu)PostgreSQL 数据库其他系统安装PostgreSQL 数据库快速使用入门登录数据库访问数据库参考pgAdmin 4pgAdmin 4 安装使用 pgAdmin 4 登录数据库参考PostgreSQL PostgreSQL 数据库安装…...

quarkus 搭建与基础开发环境配置总结

quarkus搭建与基础开发环境配置总结 大纲 基础概念quarkus2.13.7脚手架工程配置配置maven3.8.7quarkus快速启动quarkus的三种打包方式quarkus将程序打包为二进制文件window环境下quarkus云原生二进制文件打包环境搭建使用GraalVM-java11替换本地java8运行二进制文件 基础概念…...

扩散模型DDPM开源代码的剖析【对应公式与作者给的开源项目,diffusion model】

扩散模型DDPM开源代码的剖析【对应公式与作者给的开源项目&#xff0c;diffusion model】一、简介二、扩散过程&#xff1a;输入是x_0和时刻num_steps&#xff0c;输出是x_t三、逆扩散过程&#xff1a;输入x_t&#xff0c;不断采样最终输出x_0四、具体参考算法流程图五、模型mo…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...