SpringBoot+@Async注解-异步调用

news2024/11/17 21:26:16

编程开发里,使用java异步执行方法可以让程序同时处理多个请求业务,提升吞吐量来缩短业务的执行时间,在springboot的程序应用中,提供了@Async注解来实现异步执行方法。

在业务开发中,有些时候是不需要立即返回业务的处理结果,比如物联网设备上报的数据,当设备把数据上报物联网IOT平台后,订阅端通过MQ或者最简单也最常用的HTTP(S)方式订阅物联网数据,订阅端拿到数据后,一般是需要告知数据推送端(发布者)已经收到数据,在这种应用场景下,设备的数据有多种业务,报警策略的业务执行报警通知,入库时序数据库,转发到客户平台,把设备数据传入人工智能AI等。

如果这些操作使用同步的方式,那么处理完业务要花很长一段时间,如果使用的HTPPS方式订阅的,那么极有可能HTTP会话已经超时了,还没有得到返回确认,那数据推送端会认为你订阅端没收到,如果数据推送端有重发策略,那么订阅端可能会多次收到相同的数据消息,这样的话会影响具体的业务。

在springboot里使用@Async只需要两步

1.在主函数上加@EnableAsync 开启异步配置

2.在需要用到异步的地方使用@Async 申明该方法是一个异步任务

package boot.example.async;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

/**
 * 蚂蚁舞
 */
@SpringBootApplication
@EnableAsync
public class ApplicationAsync {
    public static void main( String[] args ) {
        SpringApplication.run(ApplicationAsync.class, args);
        System.out.println( "Hello Async!" );
    }

}
package boot.example.async.controller;


import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

/**
 * 蚂蚁舞
 */
@Service
public class BootAsyncService {

    @Async
    void doAsyncWork(int count){
        System.out.println("当前异步方法的计数值:"+count);

    }

}

使用http接口来测试异步

package boot.example.async.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping(value="/")
public class BootAsyncController {

    @Resource
    private BootAsyncService bootAsyncService;

    public int count = 0;
    @RequestMapping(value="/async")
    public int async() {
        count++;
        bootAsyncService.doAsyncWork(count);
        return count;
    }


}

启动springboot后在浏览器访问对应的路径便可调用异步Service方法,

在这个方法里可以处理业务逻辑,可以直接插入数据库,可以通过HTTP RestTemplate调用其他的微服务接口,在demo或者一些简单的应用中,这样做,使用起来特别方便,而且还特别流畅,提高了程序处理消息的能力,那如果说并发量不断地上升,异步的线程将会达到很多很多。

实际上使用@Async注解是有一个隐藏的坑,我用的是SpringBoot2.x,开启注解后默认使用的线程池是SimpleAsyncTaskExecutor, 只要方法上有@Async注解(@Async在有些方法上不会执行异步子线程的),当主线程调用这个方法时,会开启一个新的异步子线程去处理业务逻辑,与调用它的主线程便没有了多少关联了,那就是说不断的调用此方法,不断地去创建异步子线程,无休无止的。

如果这个方法的业务出现问题,数据库断了连接,调用其他微服务也是超时的,子线程又在不断地创建,这种情况持续下去,是会造成OOM(Out Of Memory, 内存用完了)问题的。

如此,最终的结果可能是,该服务在线啊,能访问到呢,啥原因呢?为啥我的程序停了?云商更新给我重启了?崩了!

为了尽可能的避免内存用完,使用@Async注解需要用到自定义线程池

package boot.example.async.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
public class AsyncTaskExecutorBeanConfig {
    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);        // 设置核心线程数
        executor.setMaxPoolSize(20);        // 设置最大线程数
        executor.setQueueCapacity(20);      // 设置队列容量
        executor.setKeepAliveSeconds(12);   // 设置线程活跃时间(秒)
        executor.setThreadNamePrefix("async-thread-");  // 设置默认线程名称
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); // 设置拒绝策略
        executor.setWaitForTasksToCompleteOnShutdown(true); // 等待所有任务结束后再关闭线程池
        executor.setAwaitTerminationSeconds(60);
        return executor;
    }

}

主要设置说明

executor.setCorePoolSize(10) 线程池创建的核心线程数,线程池维护线程的最少线程量,如果程序没有调用加@Async异步,线程池中的核心线程会一直存活,如果不设置默认是1

另外,这里有一个设置executor.setAllowCoreThreadTimeOut(true) 如果这样设置了,核心线程会超时关闭的,它的默认值是false

executor.setMaxPoolSize(20) 线程池内最大线程的数量,当线程数大于等于核心线程数(corePoolSize),并且任务队列(queueCapacity)满了,线程池会创建线程来处理新的业务,但是当队列满了,而且线程数已经达到了了最大线程数量(maxPoolSize),线程池只得拒绝处理业务了,根据设置的策略来丢弃子线程或者抛出异常,避免了不断创建线程造成内存用完的情况

executor.setQueueCapacity(20) 队列容量,只有当核心线程数(corePoolSize)达到最大时,新业务会放在队列中排队等待执行

executor.setKeepAliveSeconds(12) 线程活跃时间(秒)当线程空闲后,时间达到设置的时间时,线程会自动退出,直到线程数量等于核心线程数(corePoolSize) 如果设置了executor.setAllowCoreThreadTimeOut(true),那么核心线程数也要按照这个设置,超时后退出

executor.setThreadNamePrefix("async-thread-") 设置默认线程名称,这个名称会在日志内打印的,配置了日志的话,可以大概通过日志在某个时间区间内确定线程的数量,线程有没有使用到线程池的最大线程数量,还是说一直在核心线程数内,这样对于程序线上的运行有维护参考价值

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()) 设置拒绝策略,根据具体的策略执行拒绝,有效的避免了线程的不断创建

executor.setWaitForTasksToCompleteOnShutdown(true) 等待所有任务结束后再关闭线程池

executor.setAwaitTerminationSeconds(60) 等待时间,和executor.setWaitForTasksToCompleteOnShutdown(true)一起使用,在这个时间之后强制关闭所有任务了

这里备注下常用策略

.CallerRunsPolicy():交由调用方法的主线程运行,比如从controller接口层进入后的线程,如此这个主线程自己去执行该任务,那么变成了同步执行了。

.AbortPolicy():默认策略,如果线程池队列满了,丢掉后进来的线程任务,不让他们进入队列,同时抛出异常。

.DiscardPolicy():如果线程池队列满了,直接丢掉后进来的任务,不让他们进入队列,不会有任何异常抛出。

.DiscardOldestPolicy():线程队列有多大,那么队列里的线程都是最新的,老的全部丢掉,可以这样理解,两种情况,第一种情况是未进入队列的线程数小于队列中已存在的线程数,那么会丢掉队列中最前进入的的线程任务,使得未进入队列的线程数全部进入线程,第二种情况,未进入队列的线程数很多很多,队列里的线程数一直被丢掉,那么最终只留下了未进入队列线程最后的那些线程。

修改一下bean的配置,将核心线程数和队列还有异步的最大线程数降下来,方便测试

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);        // 设置核心线程数
        //executor.setAllowCoreThreadTimeOut(true);
        executor.setMaxPoolSize(4);        // 设置最大线程数
        executor.setQueueCapacity(2);      // 设置队列容量
        executor.setKeepAliveSeconds(6);   // 设置线程活跃时间(秒)
        executor.setThreadNamePrefix("async-thread-");  // 设置默认线程名称
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); // 设置拒绝策略
        executor.setWaitForTasksToCompleteOnShutdown(true); // 等待所有任务结束后再关闭线程池
        executor.setAwaitTerminationSeconds(60);
        return executor;
    }

修改service,假设work1异步方法执行要120s,work2异步方法执行要10s ,work3不用异步方法

@Service
public class BootAsyncService {

    private final Logger log =  LoggerFactory.getLogger(this.getClass());

    @Async
    void doAsyncWork1(int count) {
        log.info("1-开始-当前方法的计数值:"+count);
        try {
            // 假设需要120s处理业务
            Thread.sleep(120*1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("1-结束-当前方法的计数值:"+count);
    }

    @Async
    void doAsyncWork2(int count) {
        log.info("2-开始-当前方法的计数值:"+count);
        try {
            // 假设需要10s处理业务
            Thread.sleep(10*1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("2-结束-当前方法的计数值:"+count);
    }

    //@Async
    void doAsyncWork3(int count) {
        log.info("3-开始-当前方法的计数值:"+count);
        try {
            Thread.sleep(120*1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("3-结束-当前方法的计数值:"+count);
    }
}

controller

@RestController
@RequestMapping(value="/")
public class BootAsyncController {

    private final Logger log =  LoggerFactory.getLogger(this.getClass());
    
    @Resource
    private BootAsyncService bootAsyncService;

    public int count = 0;
    @PostMapping(value="/async1")
    public int async1() {
        count++;
        log.info("1数据消息进入:"+count);
        bootAsyncService.doAsyncWork1(count);
        return count;
    }

    @PostMapping(value="/async2")
    public int async2() {
        count++;
        log.info("2数据消息进入:"+count);
        bootAsyncService.doAsyncWork2(count);
        return count;
    }

    @PostMapping(value="/async3")
    public int async3() {
        count++;
        log.info("3数据消息进入:"+count);
        bootAsyncService.doAsyncWork3(count);
        return count;
    }

}

为了测试方便,使用了swagger2 + swagger-bootstrap-ui

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.2</version>
        </dependency>
package boot.example.async.config;

import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 *  蚂蚁舞
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket createRestApi(){
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
                .apis(RequestHandlerSelectors.any()).paths(PathSelectors.any())
                .paths(Predicates.not(PathSelectors.regex("/error.*")))
                .paths(PathSelectors.regex("/.*"))
                .build().apiInfo(apiInfo());
    }

    private ApiInfo apiInfo(){
        return new ApiInfoBuilder()
                .title("SpringBoot Async 异步Demo")
                .description("SpringBoot Async 异步Demo")
                .version("0.01")
                .build();
    }

    /**
     * http://localhost:xxxx/doc.html  地址和端口根据实际项目查看
     */


}

核心线程数 2 队列容量 2 最大线程数4

那么最大能够处理的业务是异步线程最大线程数和队列容量 4+2

如此测试9条线程 1, 2, 3, 4,5, 6, 7, 8, 9

.AbortPolicy()模式

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());

访问浏览器进行测试,当该接口调用到第七次,第八次,第九次的时候,就抛出了异常

Executor [java.util.concurrent.ThreadPoolExecutor@2e57ce8f[Running, pool size = 4, active threads = 4, queued tasks = 2, completed tasks = 0]] did not accept task: org.springframework.aop.interceptor.AsyncExecutionInterceptor$$Lambda$352/2047819523@4e39f9bb

从这个段异常信息得知,队列tasks(配置的2)用完了,最大线程数(配置的4)也用完了,那么异步线程不管是队列容量还是已经正在执行业务的异步子线程,都还没有空闲出来,此时,按照AbortPolicy()模式,丢掉后面进来的子线程,并且抛出异常,告诉你子线程用完了,别挤进来了。

再看看控制台

可以看到 外部通过浏览器访问了9次接口,那就是说,产生了9条接口接收执行的主线程,把业务交给@Async申明的异步任务,4个异步线程正在执行 = 自定义线程池最大4条异步线程(图中计数值1,2,5,6),同时队列里有2条线程正在等待执行(图中的3,4),另外还有三条线程只是在接口主线程调用了异步任务,但是抛出异常并且返回异常信息了,没有机会进入异步线程(图中的7,8,9)

分析可知,1,2,5,6,3,4是执行了业务的,7,8,9被抛弃了,抛出异常业务丢失

这里为什么线程5和6先执行业务,3和4反而等待,是因为2条核心线程数正在执行1和2,那么3和4就进入队列里,后面的5和6就线程池增加线程了,增加到最大值,4,那就是只能增加2条线程,刚好5和6去执行

.DiscardPolicy()模式

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());

DiscardPolicy()模式和AbortPolicy()模式的区别是DiscardPolicy()模式不抛出异常,其他是一样的

.DiscardOldestPolicy()模式

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); // 设置拒绝策略

访问浏览器进行测试,执行九次,可以看到返回的9

再看看控制台

可以看到 外部通过浏览器访问了9次接口,产生了9条接口接收执行的主线程,把业务交给@Async申明的异步任务,4个异步线程正在执行(图中计数值1,2,5,6),按理来说队列里有2条线程正在等待执行(图中的3,4),但是,7 8 9三条异步线程来了,如此,最老最前的3和4会被丢掉,7和8会进入异步线程队列里,但是队列的容量只有2条,那么7也要被丢弃,如此只有8和9进入队列等待,直到之前的异步线程空闲后,再来执行8和9的线程任务了。

分析可知,执行了业务的线程只有1,2,5,6,8,9 其他的3,4,7被抛弃了,没有执行业务

.CallerRunsPolicy()模式

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 设置拒绝策略

访问浏览器进行测试,执行第七次,http请求接口就卡住保持会话等待返回了,这是我再访问另一个接口(这个接口异步任务的时间稍短,很快就返回8这个数据),再访问一次 async2 这样访问总共9次

控制台输出

可以看到 外部通过浏览器访问了访问2个接口,总共9次访问,产生了9条接口接收执行的主线程,把业务交给@Async申明的异步任务,4个异步线程正在执行(图中计数值1,2,5,6),队列里有2条线程正在等待执行(图中的3,4),这是第七次访问产生了的主线程没有拿到异步的子线程,因此直接使用主线程去执行业务了(demo默认阻塞了120s),这时这个访问放一边,在通过接口2访问,那就是第 8和第 9条,可以从图里看到8和9执行的也是主线程的同步方法(demo默认阻塞时间10s,因此可以看到先结束),然后7通过主线程同步执行完业务返回,异步的1,2, 5, 6执行业务完成后空出的线程给到队列等待的线程 3和4去执行业务。

分析,这种方式,1,2,3,4,5,6,7,8,9共9条线程都进入了业务,其中 7,8,9是异步的,如果说推送端要你订阅端确认返回的话,会话时间太长,可能推送端会以为你订阅端没收到,重新发送数据,那这样业务可能就会重复执行,所以,能够满足异步返回,可以用这种方式,有时候程序就是那么2-4s的事,能接受的

在使用@Async时,有时候和Springboot事件监听一起使用

    @Resource
    private ApplicationContext applicationContext;


    @PostMapping(value="/async4")
    public int async4() {
        count++;
        log.info("4数据消息进入:"+count);
        AsyncEvent asyncEvent = new AsyncEvent();
        asyncEvent.setCount(count);
        applicationContext.publishEvent(asyncEvent);
        return count;
    }

AsyncEvent

package boot.example.async.controller;

/**
 * 蚂蚁舞
 */
public class AsyncEvent {

    private int count;

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }


}

AsyncListenerService

package boot.example.async.controller;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class AsyncListenerService {

    private final Logger log =  LoggerFactory.getLogger(this.getClass());

    @Async
    @EventListener(AsyncEvent.class)
    public void doAsyncWork4(AsyncEvent asyncEvent) {
        log.info("4-开始-当前方法的计数值:"+asyncEvent.getCount());
        try {
            // 假设需要120s处理业务
            Thread.sleep(120*1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("4-结束-当前方法的计数值:"+asyncEvent.getCount());
    }
}

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

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

相关文章

前端代码质量-圈复杂度原理和实践

1. 导读 你们是否也有过下面的想法&#xff1f; 重构一个项目还不如新开发一个项目…这代码是谁写的&#xff0c;我真想… 你们的项目中是否也存在下面的问题&#xff1f; 单个项目也越来越庞大&#xff0c;团队成员代码风格不一致&#xff0c;无法对整体的代码质量做全面的…

【LeetCode】剑指 Offer 25. 合并两个排序的链表 p145 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof/ 1. 题目介绍&#xff08;25. 合并两个排序的链表&#xff09; 输入两个递增排序的链表&#xff0c;合并这两个链表并使新链表中的节点仍然是递增排序的。 【测试用例】&#xf…

软件测试分类知识分享,第三方软件测试机构收费贵不贵?

软件测试可以很好的检验软件产品的质量以及规避产品上线之后可能会发生的错误&#xff0c;随着技术的发展&#xff0c;软件测试已经是一个完整且体系庞大的测试活动&#xff0c;不同的测试领域有着不同的测试方法、技术与名称&#xff0c;那么具体有哪些分类呢? 一、软件测试…

centos7部署KVM虚拟化

目录 centos7部署KVM虚拟化平台 1、新建一台虚拟机 2、系统内的操作 1、修改主机名 2、挂载镜像光盘 3、ssh优化 4、设置本地yum仓库 5、关闭防火墙&#xff0c;selinux 3、安装KVM 4、设置KVM网络 5、KVM部署与管理 6、使用虚拟系统管理器管理虚拟机 创建存储池 …

[曾经沧海难为水]两数求和

本来可以面试成功就差HR面试了&#xff0c;现在给我说恒英也要机试题了。我现在肝机试题吧&#xff01;大环境就是这&#xff0c;记录两周一个机试学习过程V1&#xff1a;自己写的暴力法V2&#xff1a;暴力法2:通过python的**str in list**方式逐个遍历&#xff0c;虽然代码看似…

什么叫GPC爬虫池?

什么叫GPC爬虫池&#xff1f; 答案是&#xff1a;全称光算谷歌爬虫池。 GPC爬虫池是一个深度研究谷歌SEO规律算法而创造的一种吸引谷歌爬虫的技术手段。 主要实现原理是通过建设庞大的站群系统&#xff0c;复杂的内链&#xff0c;外链结构体系&#xff0c;起到吸引谷歌爬虫&…

NetworkMiner网络取证分析工具(26)

预备知识 NetworkMiner是一款windows平台下开放源代码的网络取证分析工具&#xff0c;同时也是一款比较好的协议分析工具&#xff0c;它通过数据包嗅探或解析PCAP 文件能够检测操作系统&#xff0c;主机名和网络主机开放的端口。 除了能够进行基本的数据包抓取分析N…

剑指 Offer day5, day6

剑指 Offer day5&#xff0c; day6 二分查找和二叉树的题目 剑指 Offer 04. 二维数组中的查找 剑指 Offer 04. 二维数组中的查找 - 力扣&#xff08;Leetcode&#xff09; 依然是利用特殊的数据状况改进查找的速度&#xff0c;注意边界条件。 题解这个类比二叉树的思路非常…

第五章:C语言数据结构与算法之双向带头循环链表

系列文章目录 文章目录系列文章目录前言一、哨兵位的头节点二、双向链表的结点三、接口函数的实现1、创建结点2、初始化3、尾插与尾删4、头插与头删5、打印6、查找7、随机插入与随机删除8、判空、长度与销毁四、顺序表和链表的对比总结前言 一般题目给的单链表是无头单向非循环…

GCC编译器编译C/C++程序(一步完成、分步完成)

以下内容源于C语言中文网的学习与整理&#xff0c;非原创&#xff0c;如有侵权请告知删除。 一、编译的流程 编译C/C 程序&#xff0c;是指将C/C源代码转变为可执行程序。 这需要经历4个过程&#xff1a;预处理&#xff08;Preprocessing&#xff09;、编译&#xff08;Compi…

一次线上事故排查

问题3月1日监控系统监测到某子系统所在机器Cpu突然飙升。排查系统首先登录对应系统的机器&#xff0c;top查看机器信息&#xff0c;显示当前cpu已经到了800%top 显示800%根据top的pid查看对应服务&#xff0c;查看服务子进程排查子线程&#xff0c;发现子线程有8个都100%了&…

ESP32通过HTTP及SNTP同步网络时间

1、获取毫秒级时间 和普通系统函数相同 int get_sys_time_ms(void) {struct timeval tv_now;gettimeofday(&tv_now, NULL);int64_t time_us (int64_t)tv_now.tv_sec * 1000000L (int64_t)tv_now.tv_usec;return (int)(time_us/1000); } 2、延时毫秒级时间 void my_del…

【数据分析师求职面试指南】必备编程技能整理之Hive SQL必备用法

文章目录熟悉Python懂R语言掌握SQL大数据基础数据库常用类型多表查询更多聚合函数distinctcase when窗口函数动态更新一行变多行调优内容整理自《拿下offer 数据分析师求职面试指南》—徐粼著 第四章编程技能考查熟悉Python 懂R语言 掌握SQL 大数据基础 Hive时Hadoop的一个…

C++基础了解-10-C++ 判断

C 判断 一、C 判断 判断结构要求程序员指定一个或多个要评估或测试的条件&#xff0c;以及条件为真时要执行的语句&#xff08;必需的&#xff09;和条件为假时要执行的语句&#xff08;可选的&#xff09;。 下面是大多数编程语言中典型的判断结构的一般形式&#xff1a; …

电商 SaaS 全渠道实时数据中台最佳实践

摘要&#xff1a;本文整理自聚水潭数据专家张成玉&#xff0c;聚水潭高级数据工程师应圣楚&#xff0c;在 FFA 2022 行业案例专场的分享。本篇内容主要分为四个部分&#xff1a;实时数仓的建设和发展数据中台的产品体系及架构实时计算的实践和优化对实时计算的未来展望Tips&…

2019年MathorCup数学建模B题环形穿梭车系统的设计与调度解题全过程文档及程序

2019年第九届MathorCup高校数学建模挑战赛 B题 环形穿梭车系统的设计与调度 原题再现&#xff1a; 整体求解过程概述(摘要) 环形穿梭车系统为集多种高新技术于一体的自动搬运设备&#xff0c;行驶和输送速度快、灵活性好、自动化程度高。但由于系统采用封闭式轨道&#xff0c…

成为AI架构师的三大能力

AI架构师的定义 “AI 架构师”是以深度学习为代表的第三次AI热潮所催生的新型复合型人才&#xff0c;它的产生最本质的驱动因素是AI产业化落地应用的蓬勃发展对人才的需求&#xff0c;深度学习突出的工程属性也特别需要复合型人才来驾驭。 从字面来看&#xff0c;AI架构师的“…

Pytorch深度学习实战3-8:详解数据可视化组件TensorBoard安装与使用

目录1 什么是Tensorboard&#xff1f;2 Tensorboard安装3 Tensorboard可视化流程4 Tensorboard可视化实例4.1 常量可视化4.2 特征图可视化1 什么是Tensorboard&#xff1f; 在深度学习领域&#xff0c;网络内部如同黑箱&#xff0c;其中包含大量的连接参数&#xff0c;这给人工…

续航乱标销量低迷! 零跑汽车短时“掉”电快 ?

【锋巢网】 进入3月&#xff0c;行业复苏的景象映入眼帘&#xff0c;但是新能源车企却有人欢喜有人愁。 近日&#xff0c;各大新能源车企公布了自家2月份的销量数据&#xff0c;整体来看&#xff0c;部分新能源车企在2月份的交付量战绩显著&#xff0c;涨幅颇高。其中&#x…

class01:VUE简介与实例挂载

目录一、VUE简介1. 介绍2. 学习内容3. 引入Vue4. 全局配置5. Vue Devtools安装二、挂载Vue实例一、VUE简介 1. 介绍 Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不…