SpringBoot中使用@Async实现异步调用

SpringBoot中使用@Async实现异步调用

什么是异步调用?异步调用对应的是同步调用,同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上

一行程序执行完成之后才能执行;异步调用指程序在顺序执行时,不等待异步调用的语句返回结果就执行后面的程

序。

1、pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.6</version><relativePath/></parent><groupId>com.async</groupId><artifactId>spring-boot-async</artifactId><version>0.0.1-SNAPSHOT</version><name>spring-boot-async</name><description>Spring Boot 中使用@Async实现异步调用,加速任务执行!</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

2、同步调用

下面通过一个简单示例来直观的理解什么是同步调用。

定义 Task 类,创建三个处理函数分别模拟三个执行任务的操作,操作消耗时间随机取(10秒内)。

package com.async;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import java.util.Random;/*** @author zhangshixing* @date 2021年10月29日 14:41*/@Slf4j
@Component
public class AsyncTasks {public static Random random = new Random();public void doTaskOne() throws Exception {log.info("开始做任务一");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("完成任务一,耗时:" + (end - start) + "毫秒");}public void doTaskTwo() throws Exception {log.info("开始做任务二");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("完成任务二,耗时:" + (end - start) + "毫秒");}public void doTaskThree() throws Exception {log.info("开始做任务三");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("完成任务三,耗时:" + (end - start) + "毫秒");}}

在单元测试用例中,注入Task对象,并在测试用例中执行doTaskOnedoTaskTwodoTaskThree三个函数。

package com.async;import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.concurrent.CompletableFuture;@Slf4j
@SpringBootTest
class SpringBootAsyncApplicationTests {@Autowiredprivate AsyncTasks asyncTasks;@Testpublic void test() throws Exception {asyncTasks.doTaskOne();asyncTasks.doTaskTwo();asyncTasks.doTaskThree();}}

执行单元测试,可以看到类似如下输出:

开始做任务一
完成任务一,耗时:8577毫秒
开始做任务二
完成任务二,耗时:185毫秒
开始做任务三
完成任务三,耗时:3702毫秒

任务一、任务二、任务三顺序的执行完了,换言之doTaskOnedoTaskTwodoTaskThree三个函数顺序的执行

完成。

3、异步调用

上述的同步调用虽然顺利的执行完了三个任务,但是可以看到执行时间比较长,若这三个任务本身之间不存在依赖

关系,可以并发执行的话,同步调用在执行效率方面就比较差,可以考虑通过异步调用的方式来并发执行。

在Spring Boot中,我们只需要通过使用@Async注解就能简单的将原来的同步函数变为异步函数,Task类改在为

如下模式:

package com.async;import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;import java.util.Random;/*** @author zhangshixing* @date 2021年10月29日 15:28*/
@Slf4j
@Component
public class AsyncTasks1 {public static Random random = new Random();@Asyncpublic void doTaskOne() throws Exception {log.info("开始做任务一");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("完成任务一,耗时:" + (end - start) + "毫秒");}@Asyncpublic void doTaskTwo() throws Exception {log.info("开始做任务二");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("完成任务二,耗时:" + (end - start) + "毫秒");}@Asyncpublic void doTaskThree() throws Exception {log.info("开始做任务三");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("完成任务三,耗时:" + (end - start) + "毫秒");}}

为了让@Async注解能够生效,还需要在Spring Boot的主程序中配置@EnableAsync,如下所示:

package com.async;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;@EnableAsync
@SpringBootApplication
public class SpringBootAsyncApplication {public static void main(String[] args) {SpringApplication.run(SpringBootAsyncApplication.class, args);}}

在单元测试用例中,注入Task对象,并在测试用例中执行doTaskOnedoTaskTwodoTaskThree三个函数。

package com.async;import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.concurrent.CompletableFuture;@Slf4j
@SpringBootTest
class SpringBootAsyncApplicationTests {@Autowiredprivate AsyncTasks1 asyncTasks1;@Testpublic void test1() throws Exception {asyncTasks1.doTaskOne();asyncTasks1.doTaskTwo();asyncTasks1.doTaskThree();}}

执行单元测试,可以看到类似如下输出:

开始做任务二
开始做任务一

此时可以反复执行单元测试,您可能会遇到各种不同的结果,比如:

  • 没有任何任务相关的输出

  • 有部分任务相关的输出

  • 乱序的任务相关的输出

原因是目前doTaskOnedoTaskTwodoTaskThree三个函数的时候已经是异步执行了。主程序在异步调用

之后,主程序并不会理会这三个函数是否执行完成了,由于没有其他需要执行的内容,所以程序就自动结束

了,导致了不完整或是没有输出任务相关内容的情况。

注:@Async所修饰的函数不要定义为static类型,这样异步调用不会生效。

为了让doTaskOnedoTaskTwodoTaskThree能正常结束,假设我们需要统计一下三个任务并发执行共耗时多

少,这就需要等到上述三个函数都完成调动之后记录时间,并计算结果。那么我们如何判断上述三个异步调用是否

已经执行完成呢?我们需要使用CompletableFuture<T>来返回异步调用的结果:

package com.async;import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;import java.util.Random;
import java.util.concurrent.CompletableFuture;/*** @author zhangshixing* @date 2021年10月29日 15:47*/
@Slf4j
@Component
public class AsyncTasks2 {public static Random random = new Random();@Asyncpublic CompletableFuture<String> doTaskOne() throws Exception {log.info("开始做任务一");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("完成任务一,耗时:" + (end - start) + "毫秒");return CompletableFuture.completedFuture("任务一完成");}@Asyncpublic CompletableFuture<String> doTaskTwo() throws Exception {log.info("开始做任务二");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("完成任务二,耗时:" + (end - start) + "毫秒");return CompletableFuture.completedFuture("任务二完成");}@Asyncpublic CompletableFuture<String> doTaskThree() throws Exception {log.info("开始做任务三");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("完成任务三,耗时:" + (end - start) + "毫秒");return CompletableFuture.completedFuture("任务三完成");}}

按照如上方式改造一下其他两个异步函数之后,下面我们改造一下测试用例,让测试在等待完成三个异步调用之后

来做一些其他事情。

package com.async;import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.concurrent.CompletableFuture;@Slf4j
@SpringBootTest
class SpringBootAsyncApplicationTests {@Autowiredprivate AsyncTasks2 asyncTasks2;@Testpublic void test2() throws Exception {long start = System.currentTimeMillis();CompletableFuture<String> task1 = asyncTasks2.doTaskOne();CompletableFuture<String> task2 = asyncTasks2.doTaskTwo();CompletableFuture<String> task3 = asyncTasks2.doTaskThree();CompletableFuture.allOf(task1, task2, task3).join();long end = System.currentTimeMillis();log.info("任务全部完成,总耗时:" + (end - start) + "毫秒");}}

看看我们做了哪些改变:

  • 在测试用例一开始记录开始时间
  • 在调用三个异步函数的时候,返回CompletableFuture<String>类型的结果对象
  • 通过CompletableFuture.allOf(task1, task2, task3).join()实现三个异步任务都结束之前的阻塞效果
  • 三个任务都完成之后,根据结束时间 - 开始时间,计算出三个任务并发执行的总耗时。

执行一下上述的单元测试,可以看到如下结果:

开始做任务二
开始做任务一
完成任务二,耗时:5375毫秒
开始做任务三
完成任务一,耗时:6298毫秒
完成任务三,耗时:9388毫秒
任务全部完成,总耗时:14780毫秒

可以看到,通过异步调用,让任务一、二、三并发执行,有效的减少了程序的总运行时间。

4、千万不要这样使用@Async注解

在实际的项目中,对于一些用时比较长的代码片段或者函数,我们可以采用异步的方式来执行,这样就不会影响整

体的流程了。比如我在一个用户请求中需要上传一些文件,但是上传文件的耗时会相对来说比较长,这个时候如果

上传文件的成功与否不影响主流程的话,就可以把上传文件的操作异步化,在spring boot中比较常见的方式就是

把要异步执行的代码片段封装成一个函数,然后在函数头使用@Async注解,就可以实现代码的异步执行(当然首

先得在启动类上加上@EnableAsync注解了)。

下面这里主要讲解使用 @Async 注解中遇到的一个坑。

那么这个坑是什么呢?就是如果你在同一个类里面调用一个自己的被@Async修饰的函数时,这个函数将不会被异

步执行,它依然是同步执行的,下面来演示一下。

记得在启动类上加上@EnableAsync注解。

package com.async;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;@EnableAsync
@SpringBootApplication
public class SpringBootAsyncApplication {public static void main(String[] args) {SpringApplication.run(SpringBootAsyncApplication.class, args);}}

然后再新建一个类Task,用来放三个异步任务doTaskOnedoTaskTwodoTaskThree

package com.async;import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;import java.util.Random;/*** @author zhangshixing* @date 2021年10月29日 15:28*/
@Slf4j
@Component
public class AsyncTasks1 {public static Random random = new Random();@Asyncpublic void doTaskOne() throws Exception {log.info("开始做任务一");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("完成任务一,耗时:" + (end - start) + "毫秒");}@Asyncpublic void doTaskTwo() throws Exception {log.info("开始做任务二");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("完成任务二,耗时:" + (end - start) + "毫秒");}@Asyncpublic void doTaskThree() throws Exception {log.info("开始做任务三");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("完成任务三,耗时:" + (end - start) + "毫秒");}}

在单元测试类上注入Task,在测试用例上测试这三个方法的执行过程:

package com.async;import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@Slf4j
@SpringBootTest
class SpringBootAsyncApplicationTests1 {@AutowiredAsyncTasks1 asyncTasks1;@Testvoid contextLoads() throws Exception {asyncTasks1.doTaskOne();asyncTasks1.doTaskTwo();asyncTasks1.doTaskThree();Thread.sleep(10000);}
}

为了让这三个方法执行完,我们需要再单元测试用例上的最后一行加上一个延时,不然等函数退出了,异步任务还

没执行完。

我们启动看看效果:

开始做任务一
开始做任务二
完成任务二,耗时:314毫秒
开始做任务三
完成任务一,耗时:1365毫秒
完成任务三,耗时:3376毫秒

我们看到三个任务确实是异步执行的,那我们再看看错误的使用方法

我们在测试类里面把这三个函数再写一遍,并在测试用例上调用测试类自己的方法:

package com.async;import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.annotation.Async;import java.util.Random;@Slf4j
@SpringBootTest
class SpringBootAsyncApplicationTests2 {public static Random random = new Random();@Testvoid contextLoads() throws Exception {doTaskOne();doTaskTwo();doTaskThree();Thread.sleep(10000);}@Asyncpublic void doTaskOne() throws Exception {System.out.println("开始做任务一");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");}@Asyncpublic void doTaskTwo() throws Exception {System.out.println("开始做任务二");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");}@Asyncpublic void doTaskThree() throws Exception {System.out.println("开始做任务三");long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");}
}

我们再看看效果:

开始做任务一
完成任务一,耗时:1332毫秒
开始做任务二
完成任务二,耗时:7568毫秒
开始做任务三
完成任务三,耗时:1419毫秒

它们竟然是顺序执行的!也就是同步执行,并没有达到异步的效果。

5、用@Async会内存溢出?看看你的线程池配置了没?

上面我们介绍了如何使用@Async注解来创建异步任务,我可以用这种方法来实现一些并发操作,以加速任务的执

行效率。但是,如果只是如前文那样直接简单的创建来使用,可能还是会碰到一些问题。存在有什么问题呢?先来

思考下,下面的这个接口,通过异步任务加速执行的实现,是否存在问题或风险呢?

package com.async.controller;import com.async.AsyncTasks2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.concurrent.CompletableFuture;/*** @author zhangshixing* @date 2021年10月30日 9:15*/
@RestController
public class HelloController {@Autowiredprivate AsyncTasks2 asyncTasks;@GetMapping("/hello")public String hello() throws Exception {// 将可以并行的处理逻辑,拆分成三个异步任务同时执行CompletableFuture<String> task1 = asyncTasks.doTaskOne();CompletableFuture<String> task2 = asyncTasks.doTaskTwo();CompletableFuture<String> task3 = asyncTasks.doTaskThree();CompletableFuture.allOf(task1, task2, task3).join();return "Hello World";}
}

虽然,从单次接口调用来说,是没有问题的。但当接口被客户端频繁调用的时候,异步任务的数量就会大量增长:

3 x n(n为请求数量),如果任务处理不够快,就很可能会出现内存溢出的情况。那么为什么会内存溢出呢?根本

原因是由于Spring Boot默认用于异步任务的线程池是这样配置的:

在这里插入图片描述

图中我标出的两个重要参数是需要关注的:

  • queueCapacity:缓冲队列的容量,默认为INT的最大值(2的31次方-1)。

  • maxSize:允许的最大线程数,默认为INT的最大值(2的31次方-1)。

所以,默认情况下,一般任务队列就可能把内存给堆满了。所以,我们真正使用的时候,还需要对异步任务的执行

线程池做一些基础配置,以防止出现内存溢出导致服务不可用的问题。

5.1 配置默认线程池

默认线程池的配置很简单,只需要在配置文件中完成即可,主要有以下这些参数:

spring.task.execution.pool.core-size=2
spring.task.execution.pool.max-size=5
spring.task.execution.pool.queue-capacity=10
spring.task.execution.pool.keep-alive=60s
spring.task.execution.pool.allow-core-thread-timeout=true
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
spring.task.execution.thread-name-prefix=task-

具体配置含义如下:

  • spring.task.execution.pool.core-size:线程池创建时的初始化线程数,默认为8

  • spring.task.execution.pool.max-size:线程池的最大线程数,默认为int最大值

  • spring.task.execution.pool.queue-capacity:用来缓冲执行任务的队列,默认为int最大值

  • spring.task.execution.pool.keep-alive:线程终止前允许保持空闲的时间

  • spring.task.execution.pool.allow-core-thread-timeout:是否允许核心线程超时

  • spring.task.execution.shutdown.await-termination:是否等待剩余任务完成后才关闭应用

  • spring.task.execution.shutdown.await-termination-period:等待剩余任务完成的最大时间

  • spring.task.execution.thread-name-prefix:线程名的前缀,设置好了之后可以方便我们在日志中

    查看处理任务所在的线程池

5.2 动手试一试

首先,在没有进行线程池配置之前,可以先执行一下单元测试:

package com.async;import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.concurrent.CompletableFuture;@Slf4j
@SpringBootTest
class SpringBootAsyncApplicationTests {@Autowiredprivate AsyncTasks2 asyncTasks2;@Testpublic void test2() throws Exception {long start = System.currentTimeMillis();CompletableFuture<String> task1 = asyncTasks2.doTaskOne();CompletableFuture<String> task2 = asyncTasks2.doTaskTwo();CompletableFuture<String> task3 = asyncTasks2.doTaskThree();CompletableFuture.allOf(task1, task2, task3).join();long end = System.currentTimeMillis();log.info("任务全部完成,总耗时:" + (end - start) + "毫秒");}}

由于默认线程池的核心线程数是8,所以3个任务会同时开始执行,日志输出是这样的:

开始做任务一
开始做任务二
开始做任务三
完成任务二,耗时:770毫秒
完成任务一,耗时:7859毫秒
完成任务三,耗时:9598毫秒
任务全部完成,总耗时:9611毫秒

接着,可以尝试在配置文件中增加如下的线程池配置

spring.task.execution.pool.core-size=2
spring.task.execution.pool.max-size=5
spring.task.execution.pool.queue-capacity=10
spring.task.execution.pool.keep-alive=60s
spring.task.execution.pool.allow-core-thread-timeout=true
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
spring.task.execution.thread-name-prefix=task-

日志输出的顺序会变成如下的顺序:

开始做任务二          
开始做任务一
完成任务二,耗时:185毫秒
开始做任务三
完成任务一,耗时:2762毫秒
完成任务三,耗时:4307毫秒
任务全部完成,总耗时:4510毫秒
  • 任务一和任务二会马上占用核心线程,任务三进入队列等待。
  • 任务二完成,释放出一个核心线程,任务三从队列中移出,并占用核心线程开始处理。

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

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

相关文章

32位MCU极致性价比高速风筒方案特点--【其利天下技术】

近年来&#xff0c;伴随着人们消费升级及现代工业技术水平的提升&#xff0c;电吹风市场已经步入了绿色节能、高效多功能化的发展阶段。人们对电吹风的需求和要求都在不断增加。然而&#xff0c;传统电吹风采用交流电机&#xff0c;使用寿命有限&#xff0c;维护不方便&#xf…

数据可视化---双Y轴折线图比较

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…

【为数据之道学习笔记】5-7五类数据主题联接的应用场景

在数字化转型的背景下&#xff0c;华为的数据消费已经不再局限于传统的报表分析&#xff0c;还要支持用户的自助分析、实时分析&#xff0c;通过数据的关联&#xff0c;支持业务的关联影响分析以及对目标对象做特征识别&#xff0c;进行特定业务范围圈定、差异化管理与决策等。…

Linux CentOS7安装harbor

1、下载harbor离线包 wget https://github.com/goharbor/harbor/releases/download/v2.4.2/harbor-offline-installer-v2.4.2.tgz 2、解压安装 tar -zxvf harbor-offline-installer-v2.4.2.tgz #解压离线安装包 3、配置harbor cd harbor #切换到harbor目录下…

RabbitMQ入门指南(二):架构和管理控制台的使用

专栏导航 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、RabbitMQ架构 二、RabbitMQ管理控制台的使用 1.Exchange交换机 2.Queue队列 3.绑定Exchange交换机和Queue队列 4.发送消息 5.数据隔离 总结 前言 RabbitMQ是一个高效、可靠的开源消息队列系统…

阅览窗格功能虽然便利,但有时会出错,特别是在Word和Excel文件中更为常见

当你打开预览窗格功能时&#xff0c;每次你打开Windows文件资源管理器并选择任何文件&#xff0c;你将在屏幕的右窗格上看到该文件的小预览缩略图。 由于这个新功能&#xff0c;你可以在Windows资源管理器的右窗格上以缩略图的形式看到文件的小预览。此功能在更快地识别文件方…

Axure交互样式,交互事件,交互动作,情形基本介绍及使用,完成ERP的菜单跳转到各个页面的跳转案例,省市联动案例,下拉刷新案例

目录 一.Axure交互样式 二.交互事件 三.情形 四.交互动作 五. 完成ERP的菜单跳转到各个页面的跳转 ​编辑 五. 省市联动 ​六.下拉刷新 一.Axure交互样式 鼠标悬停;鼠标按下;选中;禁用;获取焦点; 悬停就是鼠标放上去时&#xff0c;按下是鼠标左键单击&#xff0c;选中是…

【深度学习目标检测】九、基于yolov5的路标识别(python,目标检测)

YOLOv5是目标检测领域一种非常优秀的模型&#xff0c;其具有以下几个优势&#xff1a; 1. 高精度&#xff1a;YOLOv5相比于其前身YOLOv4&#xff0c;在目标检测精度上有了显著的提升。YOLOv5使用了一系列的改进&#xff0c;如更深的网络结构、更多的特征层和更高分辨率的输入图…

14:00面试,14:05就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到12月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40…

【Vue2】Component template should contain exactly one root element.

问题描述 [plugin:vite:vue2] Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.原因分析 这个错误通常是由于 Vue 组件的模板中包含多个根元素导致的。Vue 要求组件模板中只…

HTML基础

目录 1.格式化文本1.1.& nbsp1.2.设置水平分割线粗细为51.3.粗细为5且颜色为 #0033ff1.4.对齐方式1.5.两端对齐代码小结 2.段落2.1.块级标记2.2.那些块级标记不能包含其他块级标记&#xff0c;哪些可以2.3.hr标签如何设置高度2.4.拼音音标注释ruby标记和rt/rp标记2.5.block…

纵横字谜的答案 Crossword Answers

纵横字谜的答案 Crossword Answers - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 翻译后大概是&#xff1a; 有一个 r 行 c 列 (1<r,c<10) 的网格&#xff0c;黑格为 * &#xff0c;每个白格都填有一个字母。如果一个白格的左边相邻位置或者上边相邻位置没有白格&…

三大主流前端框架介绍

在前端项目中&#xff0c;可以借助某些框架&#xff08;如React、Vue、Angular等&#xff09;来实现组件化开发&#xff0c;使代码更容易复用。此时&#xff0c;一个网页不再是由一个个独立的HTML、CSS和JavaScript文件组成&#xff0c;而是按照组件的思想将网页划分成一个个组…

JS的浅拷贝和深拷贝

首先理解什么是浅拷贝和深拷贝&#xff1a; 浅拷贝&#xff1a; 浅拷贝只会复制对象的第一层属性&#xff0c;而不会递归地复制嵌套的对象。浅拷贝仅复制对象的引用&#xff0c;新对象和原始对象仍然共享相同的引用&#xff0c;因此对新对象的修改可能会影响到原始对象。浅拷…

Java小案例-SpringBoot火车票订票购票票务系统

目录 前言 详细资料 源码获取 前言 SpringBoot火车票订票购票票务系统 前端使用技术&#xff1a;HTML5,CSS3、JavaScript、VUE等 后端使用技术&#xff1a;Spring boot&#xff08;SSM&#xff09;等 数据库&#xff1a;Mysql数据库 数据库管理工具&#xff1a;phpstud…

tp8 模型save保存方法 method not exist:think\db\Query->record

1.$schema 有一个字段存在&#xff0c;但是实际表中是缺少这个字段的 2.必填值&#xff0c;没有值

什么是关键词排名蚂蚁SEO

关键词排名是指通过搜索引擎优化&#xff08;SEO&#xff09;技术&#xff0c;将特定的关键词与网站相关联&#xff0c;从而提高网站在搜索引擎中的排名。关键词排名对于网站的流量和用户转化率具有至关重要的影响&#xff0c;因此它是SEO工作中最核心的部分之一。 如何联系蚂…

二叉树的最大深度(LeetCode 104)

文章目录 1.问题描述2.难度等级3.热门指数4.解题思路方法一&#xff1a;深度优先搜索GolangC 方法二&#xff1a;广度优先搜索GolangC 参考文献 1.问题描述 给定一个二叉树 root &#xff0c;返回其最大深度。 叉树的「最大深度」是指从根节点到最远叶子节点的最长路径上的节…

【SpringMVC】SpringMVC简介、过程分析、bean的加载和控制

文章目录 1. SpringMVC简介2. SpringMVC入门案例文件结构第一步&#xff1a;坐标导入第二步&#xff1a;创建SpringMVC容器的控制器类第三步&#xff1a;初始化SpringMVC环境&#xff0c;设定Spring加载对应的bean第四步&#xff1a;初始化Servlet容器&#xff0c;加载SpringMV…

Leetcode sql50基础题最后的4题啦

算是结束了这个阶段了&#xff0c;之后的怎么学习mysql的方向还没确定&#xff0c;但是不能断掉&#xff0c;而且路是边走边想出来的。我无语了写完了我点进去看详情都不让&#xff0c;还得重新开启计划&#xff0c;那我之前的题解不都没有了&#xff01;&#xff01; 1.第二高…