Spring Boot进阶(60):5种判断线程池任务是否全部完成的方案 | 实用技巧分享!

 1. 前言🔥

        多线程编程在现代软件开发中非常常见且重要,而线程池是多线程编程的常用技术。在使用线程池时,通常需要判断线程池中的任务是否全部完成,以便决定程序继续执行的下一步操作。本文将介绍5种判断线程池任务是否全部完成的方案,帮助开发者解决这一问题。

        所以呢,你们打算怎么处理?这将又会是干货满满的一期,全程无尿点不废话只抓重点教,具有非常好的学习效果,拿好小板凳准备就坐!希望学习的过程中大家认真听好好学,学习的途中有任何不清楚或疑问的地方皆可评论区留言或私信,bug菌将第一时间给予解惑,那么废话不多说,直接开整!Fighting!! 

2. 环境说明🔥

本地的开发环境:

  • 开发工具:IDEA 2021.3
  • JDK版本: JDK 1.8
  • Spring Boot版本:2.3.1 RELEASE
  • Maven版本:3.8.2

3. 正文🔥 

3.1 需求分析

        前言提到采用线程池来并发处理多个sql查询,其实使用线程池不麻烦,麻烦的是你要通过什么方式去统计线程池中的任务都被执行,何为都执行完了?其实这也很理解,无非你就是要把握一个点,判断【计划执行任务数】是否等于【已完成任务数】即可,如果相等则说明线程池中的任务全被执行掉了,反之就是未执行完。

        那么你就朝着这个方向去思考,有那些方式可以算出【计划执行任务数】与【已完成任务数】这两个量值?

3.2 实现概述

        统计线程池中的任务是否被全执行完的方法其实有很多很多,我给大家举几个例子: 

  • 使用 getCompletedTaskCount() 统计出【已完成任务数】和使用Java线程池中的getTaskCount() 方法来获取【总任务数】,二者进行对比即可。
  • 使用 FutureTask对象 ,等待所有任务都执行完,线程池的任务就都执行完了。
  • 使用 CountDownLatch对象 或 CyclicBarrier对象,等待所有线程都执行完之后,再执行后续流程,计数。
  • 使用isTerminated() 方法。利用线程池的终止状态(TERMINATED)来判断线程池的任务是否已经全部执行完,但想要线程池的状态发生改变,就需要调用线程池的 shutdown() 方法,不然线程池一直会处于 RUNNING 运行状态,那就没办法使用终止状态来判断任务是否已经全部执行完了,shutdown() 方法是启动线程池有序关闭的方法,它在完全关闭之前会执行完之前所有已经提交的任务,并且不会再接受任何新任务。当线程池中的所有任务都执行完之后,线程池就进入了终止状态,调用 isTerminated() 方法返回的结果就是 true 了,以这点作为依据来判断即可。
  • ...

        如果你有其他的点子,欢迎评论区交流学习。

 3.3 实现方案

3.3.1 统计完成已完成任务数

        这里通过使用getCompletedTaskCount()和getTaskCount() 方法分别统计出统计出【已完成任务数】和【总任务数】,如果相等则说明线程池的任务执行完了,否则既未执行完。

示例代码如下:

    //校验计划执行任务数 ?= 已完成任务数private static void isCompletedByTaskCount(ThreadPoolExecutor threadPool) {while (threadPool.getTaskCount() != threadPool.getCompletedTaskCount()) {}}

具体演示代码如下:

package com.example.demo.component.threadPool;import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class CountThreadTask {//创建一个最大线程数100的线程池private static ExecutorService es =new ThreadPoolExecutor(3, 100, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(100));public static void main(String[] args) throws Exception {for (int i = 1; i <= 10; i++) {int finalI = i;es.execute(() -> { //提交执行System.out.println("线程" + finalI + "执行完成!");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}});}ThreadPoolExecutor threadPool = ((ThreadPoolExecutor) es);System.out.println("线程池任务总数量:"+threadPool.getTaskCount());System.out.println("---------线程池开始执行-----------");while (true) {if (threadPool.getTaskCount() == threadPool.getCompletedTaskCount()) {System.out.println("---------线程池执行完了-----------");break;}//间隔2s查询一次Thread.sleep(2000);System.out.println("线程池还未执行完,敬请等待!已完成的任务数量:"+threadPool.getCompletedTaskCount());}}}

执行main函数,结果控制台打印示例如下,仅供参考:

方法说明及拓展:

  • getTaskCount():返回线程池计划执行的任务总数。注意:由于任务和线程的状态可能在计算过程中动态变化,因此该方法返回值只是一个近似值,不是精准的。
  • getCompletedTaskCount():返回线程池中已完成的任务数,注意:跟getTaskCount()方法一致,该方法返回值也是一个近似值。
  • getPoolSize():返回线程池当前的线程数量。
  • getActiveCount():返回当前线程池中正在执行任务的线程数量。

方式总结:

        由于getTaskCount() 与 getCompletedTaskCount()方法返回值都是一个近似值而不是精确值,固结果可能有一定的偏差,这也是该方式的一大缺点。

3.3.2 使用 FutureTask 

        与方式1不同的是,FutrueTask 可以弥补它的弊端,使用它可以精准获取任务结果,调用每个 FutrueTask 对象的 get() 方法就是等待该任务执行完,如下代码所示:

package com.example.demo.component.threadPool;import java.util.concurrent.*;/*** 使用 FutrueTask 等待线程池执行完全部任务*/
public class FutureTaskTask {//创建一个最大线程数100的线程池private static ExecutorService es =new ThreadPoolExecutor(4, 100, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(100));public static void main(String[] args) throws ExecutionException, InterruptedException {// 创建任务1FutureTask<Integer> task1 = new FutureTask<>(() -> {System.out.println("---Task 1 开始执行---");Thread.sleep(2000);System.out.println("------Task 1 执行结束------");return 1;});// 创建任务2FutureTask<Integer> task2 = new FutureTask<>(() -> {System.out.println("---Task 2 开始执行---");Thread.sleep(3000);System.out.println("------Task 2 执行结束------");return 2;});// 创建任务3FutureTask<Integer> task3 = new FutureTask<>(() -> {System.out.println("---Task 3 开始执行---");Thread.sleep(1000);System.out.println("------Task 3 执行结束------");return 3;});// 创建任务4FutureTask<Integer> task4 = new FutureTask<>(() -> {System.out.println("---Task 4 开始执行---");Thread.sleep(500);System.out.println("------Task 4 执行结束------");return 4;});// 提交4个任务给线程池es.submit(task1);es.submit(task2);es.submit(task3);es.submit(task4);// 等待所有任务执行完毕task1.get();task2.get();task3.get();task4.get();//执行完毕System.out.println("线程池执行完了!");}
}

执行main函数,结果控制台打印示例如下,仅供参考:

3.3.3 使用CountDownLatch 

        CountDownLatch身为同步工具类,作用之一可协调多个线程之间的同步,或者说接通线程之间的通信(而不是互斥)。CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后再继续执行。其中,计数器初始值为全线程的数量,当每一个线程完成自己任务后,计数器的值就会自动减1;当计数器的值 = 0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。

        接下来给大家演示下,如何巧妙利用CountDownLatch达到统计线程池所有线程都被执行完的需求?请看示例代码:

package com.example.demo.component.threadPool;import java.util.concurrent.*;/*** 使用CountDownLatch*/
public class CountDownLatchTask {//创建一个最大线程数100的线程池private static ExecutorService es =new ThreadPoolExecutor(1, 100, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(100));public static void main(String[] args) throws Exception {//计数器,判断线程是否执行结束//初始值为10CountDownLatch taskLatch = new CountDownLatch(10);for (int i = 0; i < 10; i++) {es.execute(() -> { //提交执行taskLatch.countDown();System.out.println("当前计数器值为:" + taskLatch.getCount());try {//模拟线程执行方法,执行1sThread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}//当前线程阻塞,等待计数器置为0taskLatch.await();System.out.println("线程池执行完了!");}}

执行main函数,结果控制台打印示例如下,仅供参考:

方式总结:

        虽然使用CountDownLatch可达到统计线程是否被执行完,该方式使用起来代码简洁优雅,不需要对线程池进行操作。但由于CountDownLatch是一次性的,计数器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。

3.3.4 使用CyclicBarrier 

        CyclicBarrier 和 CountDownLatch 类似,你可以把它理解为一个可以重复使用的循环计数器,CyclicBarrier 可调用 reset() 方法将自己重置到初始状态,这是与CountDownLatch不一样的特性,那具体如何使用CyclicBarrier达到统计线程池所有线程都被执行完的需求吧,具体实现代码如下,仅供参考:

package com.example.demo.component.threadPool;import java.util.Random;
import java.util.concurrent.*;/*** 使用CyclicBarrier*/
public class CyclicBarrierTask {//创建一个最大线程数100的线程池private static ExecutorService es =new ThreadPoolExecutor(5, 100, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(100));public static void main(String[] args) throws InterruptedException {//任务总数final int taskCount = 5;//循环计数器CyclicBarrier cyclicBarrier = new CyclicBarrier(taskCount, new Runnable() {@Overridepublic void run() {// 线程池执行完System.out.println("---------线程池执行完了-----------");}});// 添加任务for (int i = 0; i < taskCount; i++) {final int finalI = i;es.submit(new Runnable() {@Overridepublic void run() {try {//随机休眠1-4秒TimeUnit.SECONDS.sleep(new Random().nextInt(5));System.out.println("任务" + finalI + "执行完成");// 线程执行完cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}});}}
}

执行main函数,结果控制台打印示例如下,仅供参考:

3.3.5 使用isTerminated()

        使用线程池的 isTerminated() 方法,在执行 shutdown() 进行线程池的关闭后, 隔间调用isTerminated()判断线程池中的所有任务是否已经完成即可。那具体如何使用 isTerminated() 方法达到统计线程池所有线程都被执行完的需求吧,具体实现代码如下,仅供参考:

package com.example.demo.component.threadPool;import java.util.concurrent.*;/*** 使用isTerminated()*/
public class IsTerminatedTask {//创建一个最大线程数100的线程池private static ExecutorService es =new ThreadPoolExecutor(4, 100, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(100));public static void main(String[] args) throws Exception {for (int i = 1; i <= 10; i++) {int finalI = i;es.execute(() -> { //提交执行System.out.println("线程" + finalI + "执行完成!");try {//模拟线程执行过程Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}//关闭线程池es.shutdown();//隔间1s判断是否执行完了,如果所有任务在关闭后完成,返回true。while (!es.isTerminated()) {Thread.sleep(1000);}System.out.println("---------线程池执行完了-----------");}
}

执行main函数,结果控制台打印示例如下,仅供参考:

在上述代码演示中,在主线程中进行循环判断,全部任务是否已经完成。 

拓展:

  • shutdown() :对线程池进行有序关闭。调用该方法后,线程池将不再接受新的任务,但会继续执行已提交的任务。如果线程池已经处于关闭状态,则对该方法的调用没有额外的作用。
  • isTerminated() :判断线程池中的所有任务是否在关闭后完成。只有在调用了shutdown()或shutdownNow()方法后,所有任务执行完毕,才会返回true。需要注意的是,在调用shutdown()之前调用isTerminated()方法始终返回false值的。

3.4 小结

        如上,我总共诺列了五种解决思路,小伙伴在面对该场景时,猜想第一感觉想到的会是方式1跟方式5吧,但是这五种方式,实现思路上各有优劣,如下bug菌就简单给同学们分析下其中的关系利弊,仅供参考。

3.4.1 使用getCompletedTaskCount()和getTaskCount() 方法

优点:使用它可不需要进行线程池的关闭,避免了创建线程池及销毁所带来的内存开销。

缺点:使用它两方法返回的都是一个近似值,而且进行线程判断局限很大,要保证在循环判断过程中没有产生新的任务,否则该方式就统计失效了。

3.4.2 使用 FutureTask

优点:使用其方法就是主打一个精确值,使用简单优雅,不需要对线程池有任何的操作。

缺点:每个提交给线程池的任务都会关联一个FutureTask对象,这就可能会损耗额外的内存开销。如果需要处理大量的任务,可能会占用较大的内存资源。

3.4.3 使用CountDownLatch 

优点:使用简单优雅,不需要对线程池有任何的操作。

缺点:使用CountDownLatch 计数器只能使用一次,CountDownLatch 创建之后不能重复使用,而且需要提前知道线程的数量,性能较差,还需要在线程代码块内加上异常判断,否则在 countDown()之前发生异常而没有处理,就会导致主线程永远阻塞在 await 。

3.4.4 使用CyclicBarrier 

优点:使用简单优雅,计数器可重置进行重复使用。

缺点:使用难度较高。相比CountDownLatch而言,CyclicBarrier 无论从设计还是使用,复杂度都高于CountDownLatch,相比 CountDownLatch 而言它的优点就是可以重复使用。

3.4.5 使用isTerminated()

优点:使用简单优雅。

缺点:使用场景受限,需要shutdown()关闭线程池。因为日常使用是会将线程池注入到Spring容器里,然后各个组件中都统一用同一个线程池,不能直接关闭线程池。

... ...

        以上提供了五种不同的思路对其进行求解,且分析了这五种方式的使用优劣,希望对同学们有所帮助。如果有小伙伴还有其他的奇思妙想,欢迎评论区大胆交流,一起学习。

4. 热文推荐🔥

滴~如下推荐【Spring Boot 进阶篇】的学习大纲,请小伙伴们注意查收。

Spring Boot进阶(01):Spring Boot 集成 Redis,实现缓存自由

Spring Boot进阶(02):使用Validation进行参数校验

Spring Boot进阶(03):如何使用MyBatis-Plus实现字段的自动填充

Spring Boot进阶(04):如何使用MyBatis-Plus快速实现自定义sql分页

Spring Boot进阶(05):Spring Boot 整合RabbitMq,实现消息队列服务

Spring Boot进阶(06):Windows10系统搭建 RabbitMq Server 服务端

Spring Boot进阶(07):集成EasyPoi,实现Excel/Word的导入导出

Spring Boot进阶(08):集成EasyPoi,实现Excel/Word携带图片导出

Spring Boot进阶(09):集成EasyPoi,实现Excel文件多sheet导入导出

Spring Boot进阶(10):集成EasyPoi,实现Excel模板导出成PDF文件

Spring Boot进阶(11):Spring Boot 如何实现纯文本转成.csv格式文件?

Spring Boot进阶(12):Spring Boot 如何获取Excel sheet页的数量?

Spring Boot进阶(13):Spring Boot 如何获取@ApiModelProperty(value = “序列号“, name = “uuid“)中的value值name值?

Spring Boot进阶(14):Spring Boot 如何手动连接库并获取指定表结构?一文教会你

Spring Boot进阶(15):根据数据库连接信息指定分页查询表结构信息

Spring Boot进阶(16):Spring Boot 如何通过Redis实现手机号验证码功能?

Spring Boot进阶(17):Spring Boot如何在swagger2中配置header请求头等参数信息

Spring Boot进阶(18):SpringBoot如何使用@Scheduled创建定时任务?

Spring Boot进阶(19):Spring Boot 整合ElasticSearch

Spring Boot进阶(20):配置Jetty容器

Spring Boot进阶(21):配置Undertow容器

Spring Boot进阶(22):Tomcat与Undertow容器性能对比分析

Spring Boot进阶(23):实现文件上传

Spring Boot进阶(24):如何快速实现多文件上传?

Spring Boot进阶(25):文件上传的单元测试怎么写?

Spring Boot进阶(26):Mybatis 中 resultType、resultMap详解及实战教学

Spring Boot进阶(27):Spring Boot 整合 kafka(环境搭建+演示)

Spring Boot进阶(28):Jar包Linux后台启动部署及滚动日志查看,日志输出至实体文件保存

Spring Boot进阶(29):如何正确使用@PathVariable,@RequestParam、@RequestBody等注解?不会我教你,结合Postman演示

Spring Boot进阶(30):@RestController和@Controller 注解使用区别,实战演示

...

5. 文末🔥

        如果想系统性的学习Spring Boot,小伙伴们直接订阅bug菌专门为大家创建的Spring Boot专栏《滚雪球学Spring Boot》从入门到精通,从无到有,从零到一!以知识点+实例+项目的学习模式由浅入深对Spring Boot框架进行学习&使用。

        如果你有一定的基础却又想精进Spring Boot,那么《Spring Boot进阶实战》将会是你的最好的选择;此栏进行知识点+实例+项目的学习方式全面深入框架剖析及各种高阶玩法,励志打造全网最全最新springboot学习专栏,投资学习自己性价比最高。

        本文涉及所有源代码,均已上传至github开源,供同学们一对一参考,GitHub,同时,原创开源不易,欢迎给个star🌟,想体验下被加Star的感jio,非常感谢 ❗

       我是bug菌,一名想走👣出大山改变命运的程序猿。接下来的路还很长,都等待着我们去突破、去挑战。来吧,小伙伴们,我们一起加油!未来皆可期,fighting!

关注公众号,获取最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等硬核资源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/109839.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android GreenDao数据库升级(附Demo)

前言 大家好久不见&#xff0c;一转眼马上八月份下旬了&#xff0c;最近由于工作比较忙&#xff0c;没时间给大家更新博文。百忙之中抽出时间&#xff0c;给大家来更新一篇关于GreenDao3数据库的升级。 关于GreenDao的详细介绍以及一些逻辑性的增、删、改、查等&#xff0c;可以…

Linux(实操篇一)

Linux实操篇 Linux(实操篇一)1. 常用基本命令1.1 帮助命令1.1.1 man获得帮助信息1.1.2 help获得shell内置命令的帮助信息1.1.3 常用快捷键 1.2 文件目录类1.2.1 pwd显示当前 工作目录的绝对路径1.2.2 ls列出目录的内容1.2.3 cd切换目录1.2.4 mkdir创建一个新的目录1.2.5 rmdir删…

jvm的内存划分区域

jvm划分5个区域&#xff1a; java虚拟机栈、本地方法栈、堆、程序计数器、方法区。 各个区各自的作用&#xff1a; 1.本地方法栈&#xff1a;用于管理本地方法的调用&#xff0c;里面并没有我们写的代码逻辑&#xff0c;其由native修饰&#xff0c;由 C 语言实现。 2.程序计数…

隐秘的角落:Java连接Oracle提示Connection timed out

前言 这个报错相信各位后端开发都不陌生&#xff0c;大体的原因就那么几种&#xff1a; 检查网络连接&#xff1a;确保您的计算机与数据库服务器之间的网络连接正常。尝试通过其他方式验证您的网络连接是否正常。 检查数据库服务器状态&#xff1a;确保数据库服务器正在运行&…

解决redis-server.exe不是内部或外部命令

报错&#xff1a;redis-server.exe不是内部或外部命令 原因&#xff1a;未进入到redis的安装目录下 解决&#xff1a;先找到redis安装路径&#xff0c;复制之后&#xff0c;在终端中输入cd xxxxx(redis的安装路径)&#xff0c;进入安装目录之后再次输入redis-server.exe就成功了…

【腾讯云Cloud Studio实战训练营】React 快速构建点餐页面+Python 拼图小游戏

文章目录 一、腾讯云 Cloud Studio 概述1.1 腾讯云 Cloud Studio 简介1.2 腾讯云 Cloud Studio 功能特点1.3 腾讯云 Cloud Studio 产品优势 二、Cloud Studio界面功能介绍2.1 注册登录2.1.1 新注册用户有免费的3000分钟体验 2.2 界面功能介绍2.2.1 空间模板2.2.2 开发空间关闭空…

android framework之Applicataion启动流程分析

Application启动流程分析 启动方式一&#xff1a;通过Launcher启动app 启动方式二&#xff1a;在某一个app里启动第二个app的Activity. 以上两种方式均可触发app进程的启动。但无论哪种方式&#xff0c;最终通过通过调用AMS的startActivity()来启动application的。 根据上图…

【音视频处理】转编码H264 to H265,FFmpeg,代码分享讲解

大家好&#xff0c;欢迎来到停止重构的频道。 本期我们讨论音视频文件转编码&#xff0c;如将视频H264转H265等。 内容中所提及的代码都会放在GitHub&#xff0c;感兴趣的小伙伴可以到GitHub下载。 我们按这样的顺序展开讨论&#xff1a;​ 1、 编码的作用 2、 转编码的…

npm yarn pnpm npx nvm 命令怎么区分怎么用

npm​​​​​​​ 包管理器&#xff0c;可以用来安装、卸载、更新和管理各种包npm的package.json中文文档 参数 - install&#xff1a;安装一个或多个包。例如&#xff1a;npm install 。 uninstall&#xff1a;卸载一个包。例如&#xff1a;npm uninstall 。 update&#xf…

【stable-diffusion使用扩展+插件和模型资源(下)】

插件模型魔法图片等资源&#xff1a;https://tianfeng.space/1240.html 书接上文&#xff1a;&#xff08;上&#xff09; 插件推荐 1.lobe theme lobe theme是一款主题插件&#xff0c;直接可以在扩展安装 界面进行了重新布局&#xff0c;做了一些优化&#xff0c;有兴趣的…

【业务功能篇85】微服务-springcloud-Nginx-反向代理-网关

Nginx域名 1.hosts文件 在c:/window/system32/drivers/etc/hosts文件&#xff0c;我们在这个文件中添加 192.168.56.100 msb.mall.com注意如果是没有操作权限&#xff0c;那么点击该文件右击属性&#xff0c;去掉只读属性即可 通过这个域名访问到Nginx服务 2.Nginx的方向代…

【C++多线程】C++11互斥锁和条件变量实现生产者消费者模型

先看几个问题&#xff0c;第三个问题可以先看代码然后再理解 Q1&#xff1a;临界区在哪 A1: 队列中元素在「生产者生产&#xff08;push&#xff09;」和「消费者消费&#xff08;pop&#xff09;」时就是临界区 Q2&#xff1a;同步操作在哪 A2: 很显然&#xff0c;队列只有…

基于Java+SpringBoot+Vue前后端分离社区医院管理系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

若依移动端Ruoyi-App 项目的后端项目入门

后端项目运行 运行报错 Error creating bean with name sysConfigServiceImpl: Invocation of init method failed 数据库创建了。 代码连接数据库地方了也匹配上了。但是还是报错。 分析 &#xff1a; 想起来我电脑从来没有安装过redis 下载安装redis到windows 链接&…

煤质软化:提升采矿效率的关键技术

煤质软化&#xff1a;提升采矿效率的关键技术 随着煤炭的广泛应用和能源需求的增长&#xff0c;对煤炭采矿和利用的要求也不断提高。传统的煤炭采矿方法往往面临一些困难和挑战&#xff0c;如厚层、高硬度以及强抗压性等问题&#xff0c;导致采掘效率低下、操作困难&#xff0c…

4.14 HTTPS 中 TLS 和 TCP 能同时握手吗?

目录 实现HTTPS中TLS和TCP同时握手的前提&#xff1a; 什么是TCP Fast Open&#xff1f; TLS v1.3 TCP Fast Open TLSv1.3 HTTPS都是基于TCP传输协议实现的&#xff0c;得先建立完可靠得TCP连接才能做TLS握手的事情。 实现HTTPS中TLS和TCP同时握手的前提&#xff1a; 1、…

9.4 集成功率放大电路

OTL、OCL 和 BTL 电路均有各种不同输出功率和不同电压增益的集成电路。应当注意&#xff0c;在使用 OTL 电路时&#xff0c;需外接输出电容。为了改善频率特性&#xff0c;减小非线性失真&#xff0c;很多电路内部还引入深度负反馈。这里以低频功放为例。 一、集成功率放大电路…

Unity编辑器扩展:提高效率与创造力的关键

Unity编辑器扩展&#xff1a;提高效率与创造力的关键 前言 一、理解Unity编辑器二、扩展Unity编辑器的意义三、扩展Unity编辑器的必要性四、Unity编辑器的扩展方式五、扩展Unity编辑器的步骤六、Unity编辑器扩展的应用案例七、总结 前言 Unity是一款广泛使用的游戏开发引擎&am…

机器学习十大算法之七——随机森林

0 引言 集成学习&#xff08;ensemble learning&#xff09;是时下非常流行的机器学习算法&#xff0c;它本身不是一个单独的机器学习算法&#xff0c;而是通过在数据上构建多个横型&#xff0c;集成所有模型的建模结果&#xff0c;基本上所有的机器学习领域都可以看到集成学习…

前端将file文件传给后台,后台将文件传给前台(包含上传下载)

前端将file文件传给后台&#xff0c;后台将文件传给前台&#xff08;包含上传下载&#xff09; 在开发过程中&#xff0c;经常会遇见对文件的处理。 例如&#xff1a;在上传、下载文件时&#xff0c;需要在前端选完文件传到后台传到服务器&#xff1b;或者文件从后台&#xf…