Springboot中开启多线程,实现异步非阻塞、异步阻塞、有无返回值的场景

48 篇文章 4 订阅
3 篇文章 0 订阅

需求背景

近期项目已上线,闲着没事就对功能进行性能测试,测着测着感觉部分功能效果不是很理想,于是就想着使用多线程的方式对部分接口进行优化,顺便在这里记录下如何选择使用多线程。

实现多线程有两种开启方式:分别是使用xml文件配置和注解的方式,想要简单方便的肯定优先使用注解啊,在Springboot中使用注解开启多线程主要包含以下步骤:

1、项目启动类上添加@EnableAsync注解,表示开启支持异步任务;
2、创建配置线程池,使用@Configuration和@Bean注解交由Spring容器管理;
3、使用@Async注解标记异步任务;

基本概念

步骤已经清楚了,接下来我们先来大概了解下概念:

1、同步:同步是指一个进程在执行某个请求的时候,如果该请求需要一段时间才能返回信息,那么这个进程会一直等待下去,直到收到返回信息才继续执行下去;(举个例子:当你去上厕所时只有一个卫生间,恰好卫生间有人正在使用,这个时候你必须要等待上个人使用完毕);其实这个概念也可以称为阻塞状态。
2、 异步:异步是指进程不需要一直等待下去,而是继续执行下面的操作,不管其他进程的状态,当有信息返回的时候会通知进程进行处理,这样就可以提高执行的效率了,即异步是我们发出的一个请求,该请求会在后台自动发出并获取数据,然后对数据进行处理,在此过程中,我们可以继续做其他操作,不管它怎么发出请求,不关心它怎么处理数据;(举个例子:当你去上厕所时有多个卫生间,部分卫生间被占用,但是可以使用别的卫生间,不需要等待别人,甚至还能边上边来一根)。这个概念也可以称为非阻塞状态。

代码实现

基本步骤和概念都清楚了,那就开始上代码,根据不同的场景需求来编写不同的多线程任务。

场景一(异步非阻塞且无返回值)

1、启动类添加 @EnableAsync 注解;
在这里插入图片描述
2、创建配置线程池(可复制粘贴,基本通用);

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
 * @Author: ljh
 * @ClassName AsynConfig
 * @Description TODO
 * @date 2023/10/21 11:03
 * @Version 1.0
 */
@Configuration
public class AsyncConfig {
    @Bean("asyncconfig")
    public Executor doSomethingExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数:线程池创建时候初始化的线程数
        executor.setCorePoolSize(10);
        // 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(20);
        // 缓冲队列:用来缓冲执行任务的队列
        executor.setQueueCapacity(500);
        // 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(60);
        // 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
        executor.setThreadNamePrefix("async-");
        // 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程)
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

3、编写异步任务,使用 @Async 注解进行标记;

	@Async("asyncconfig")
    @Override
    public void asyncText(Integer num) {
        System.err.println(num);
    }

4、调用异步任务进行测试,注意:调用方法和被调用任务不可以放在同一个类中,否则会导致@Async失效,我是分别放在了Controller和ServiceImpl层;

    @ApiOperation("测试异步任务")
    @PostMapping("/asyncText")
    public void asyncText() {
        System.err.println("==========主线程开始==========");
        for(int i = 0; i < 10; i++){
            System.err.println("----------子线程开始----------");
            //调用ServiceImpl层编写的异步任务
            baseInfoService.asyncText(i);
            System.err.println("----------子线程结束----------");
        }
        System.err.println("==========主线程结束==========");
    }

打印结果:

==========主线程开始==========
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
0
1
----------子线程开始----------
2
----------子线程结束----------
----------子线程开始----------
3
----------子线程结束----------
----------子线程开始----------
4
----------子线程结束----------
----------子线程开始----------
5
----------子线程结束----------
----------子线程开始----------
6
7
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
==========主线程结束==========
8
9

由以上打印结果进行分析:可以看到在主线程结束后依然打印了8、9,这说明主线程和子线程是异步的,主线程是不需要等待子线程是否全部执行完毕,这就是异步非阻塞的形式。

场景二(异步非阻塞且有返回值)

异步非阻塞且有返回值的场景其实是不存在的,为什么这样说呢?因为想要获取子线程的返回值,是不是必须要等待子线程执行完毕,如果不等待子线程执行完毕那么获取到的值只能是null,只有等待子线程执行完毕才能获取到想要的值,要等待只能是阻塞,所以异步非阻塞且有返回值的场景几乎是不存在的,除非你子线程有返回值但是结果又对你来说不重要没影响,这样的话还要返回值干什么呢。

场景三(异步阻塞且无返回值)

1、2步骤与前面一致,这里不在赘述;
3、编写异步任务,使用 @Async 注解进行标记;

@Async("asyncconfig")
    @Override
    public void asyncText(Integer num,CountDownLatch latch) {
        System.err.println(num);
        latch.countDown();
    }

4、调用异步任务进行测试,注意:调用方法和被调用任务不可以放在同一个类中,否则会导致@Async失效,我是分别放在了Controller和ServiceImpl层;

	@ApiOperation("测试同步异步任务")
    @PostMapping("/asyncText")
    public void asyncText() {
        System.err.println("==========主线程开始==========");
        CountDownLatch latch = new CountDownLatch(10);
        for(int i = 0; i < 10; i++){
            System.err.println("----------子线程开始----------");
            baseInfoService.asyncText(i,latch);
            System.err.println("----------子线程结束----------");
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.err.println("==========主线程结束==========");
    }

打印结果

==========主线程开始==========
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
1
0
----------子线程结束----------
----------子线程开始----------
3
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
2
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
4
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
5
6
7
8
9
==========主线程结束==========

由以上打印结果进行分析:在这里可以看到,主线程任务是等待全部子线程执行完毕后才执行结束的,也就是在执行异步子线程时阻塞当前主线程必须等待子线程全部执行完毕后才能继续执行主线程,实现方式就是使用了 CountDownLatch 类应用计数器的原理,使用CountDownLatch时需要先定义计数器的大小,因为我这里是写的循环,所以计数器大小就是循环的次数,异步任务中的countDown() 方法是每次计数器进行减一,await() 方法则是阻塞当前线程,然后等待计数器为0时才会被唤醒当前线程继续执行。

场景四(异步阻塞且有返回值)

1、2步骤与前面一致,这里不在赘述;
3、编写异步任务,使用 @Async 注解进行标记;

@Async("asyncconfig")
    @Override
    public CompletableFuture<String> asyncText(Integer num) {
        return CompletableFuture.completedFuture(String.valueOf(num));
    }

4、调用异步任务进行测试,注意:调用方法和被调用任务不可以放在同一个类中,否则会导致@Async失效,我是分别放在了Controller和ServiceImpl层;

	@ApiOperation("测试同步异步任务")
    @PostMapping("/asyncText")
    public void asyncText() {
        System.err.println("==========主线程开始==========");
        List<CompletableFuture<String>> list = new ArrayList<>();
        for(int i = 0; i < 10; i++){
            System.err.println("----------子线程开始----------");
            CompletableFuture<String> future = baseInfoService.asyncText(i);
            list.add(future);
            System.err.println("----------子线程结束----------");
        }
        for(CompletableFuture<String> str : list){
            try {
                //阻塞,直至 str的异步线程执行完毕
                CompletableFuture.allOf(str).join();
                System.err.println(str.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        System.err.println("==========主线程结束==========");
    }

执行结果

==========主线程开始==========
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
----------子线程开始----------
----------子线程结束----------
0
1
2
3
4
5
6
7
8
9
==========主线程结束==========

由以上打印结果进行分析:在这里呢主要是使用到了 CompletableFuture ,首先需要定义异步任务的返回值类型为 CompletableFuture< String >CompletableFuture< Integer> 或其它需要的类型,调用异步任务后需要先将结果存起来,为什么不直接获取结果而是存起来呢,因为任务是异步的,如果子线程没有执行完毕获取的结果只是null,所以结果集存放起来后呢使用 CompletableFuture.allOf(str).join() 方式阻塞主线程必须等待子线程执行完毕,然后才能使用 get() 方法来获取最终的结果。

总结

开启多线程异步的方式有很多种,不单单局限以上方式,感兴趣的可以自行研究测试下,比如还可以使用 ThreadPoolTaskExecutor 来开启多线程,然后分别使用对应的 execute()submit() 方法实现无返回值和有返回值的效果;以上内容均为个人理解,如存在不当欢迎提出改进。

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
python入门到高级全栈工程师培训视频学习资料;本资料仅用于学习,请查看后24小时之内删除。 【课程内容】 第1章 01 计算机发展史 02 计算机系统 03 小结 04 数据的概念 05 进制转换 06 原码补码反码 07 物理层和数据链路层 08 网络层和arp协议 09 传输层和应用层 第2章 01 上节课复习 02 arp协议复习 03 字符编码 第3章 01 网络基础和dos命令 02 为何学习linux 03 课程内容介绍 04 操作系统内核与系统调用 05 操作系统安装原理 06 linux操作系统安装 07 初识linux命令 08 linux操作系统目录结构 09 目录及文件操作 第4章 01 上节课复习 02 创建用户相关的文件 03 用户增删该查及组相关操作 04 对文件的权限管理 05 对目录的权限管理 06 权限管理补充 07 属主属组及基于数字的权限管理 第5章 01 上节课复习 02 文件合并与文件归档 03 文件归档与两种压缩方 04 vim编辑器 05 系统启动流程 06 grub加密 07 bios加密 08 top命令 09 free命令 10 进程管理 第6章 01 上节课复习 02 磁盘分区 03 文件系统与挂载 04 挂载信息讲解 05 磁盘用满的两种情况 06 软连接和硬链接 07 软连接和硬链接补充 第7章 01 ip地址与子网划分 02 ip地址配置 03 虚拟机网络模 04 三层隔离验证试验 第8章 01 上节课复习 02 软件包介绍 03 rpm软件包管理 04 yum软件包管理 05 源码安装python3.5 06 ssh服务 07 apache服务 08 samba服务 第9章 01 Python开发系列课程概要 02 Python作业要求以及博客 03 编程语言介绍 04 Python种类介绍 05 Python安装以及环境变量的操作 06 Python初识以及变量 07 Python条件语句和基本数据类型 08 Python while循环语句以及练习题 09 练习题讲解 第10章 01 上节内容回顾以及补充 02 上周作业实现 03 Pycharm的安装和使用 04 Python 运算符 05 Python 运算符以及总结 06 Python 基本数据类型介绍 07 Python 整形的魔法 08 Python 字符串的魔法 第11章 01 Python 字符串的魔法 02 Python range的用法以及练习 03 Python 课上练习解释 04 Python 基础知识练习题试题 第12章 01 今日内容介绍以及基础测试题答案讲解 02 Python 列表的魔法 03 Python 元组的魔法 04 Python 字典的魔法 05 Python 错误更正:布尔值可以作为字典的key 06 Python 今日内容整理 第13章 第13章共1课 第14章 01 数据类型和变量总结 02 集合定义和基本操作方法 03 集合关系运算交,差,并集 04 集合的其他内置方法 05 集合补充 06 百分号字符串拼接 07 format字符串格化 08 数学意义的函数与python的函数 09 为何要有函数 10 函数返回值 11 可变长参数 第15章 01 上节课复习 02 全局变量与局部变量 03 风湿理论之函数即变量 04 函数递归 05 函数递归补充 第16章 01 上节课回顾 02 函数作用域 03 函数作用域补充 04 匿名函数 05 函数编程介绍 06 函数编程尾递归调用优化 07 map函数 08 map函数filter函数 09 reduce函数 10 map reduce filter总结 11 内置函数part1 第17章 01 课前吹牛 02 zip方法 03 max和min高级使用 04 其他内置函数 05 文件操作的其他模 第18章 01 上节课复习 02 文件处理b模 03 文件操作的其他方法 04 文件seek方法补充 05 迭代器协议和for循环工作机制 06 迭代器补充 07 三元运算,列表解析,生成器表达 第19章 01 生成器函数 02 生成器函数的好处 03 母鸡下蛋的传说 04 生成器特性阐释 05 生产者消费者模型 06 第三次作业讲解 第20章 01 上节课回顾 02 装饰器基本理论 03 高阶函数使用 04 函数闭包 05 函数闭包装饰器基本实现 06 函数闭包加上返回值 07 函数闭包加上参数 08 函数闭包补充:解压序列 09 函数闭包为函数加上认证功能 10 函数闭包模拟session 11 函数闭包装饰器运行流程 12 函数闭包带参数装饰器 第21章 01 查询功能 02 修改功能 03 程序的解耦 04 module模块和包的介绍 05 模块的执行以及__name__ 06 关于模块的介绍 07 time时间模块 08 random模块 第22章 01 模块的补充 02 sys修改环境变量 03 BASEDIR的介绍 04 os模块的介绍 05 sys模块的介绍 06 json模块 07 pickle模块 08 shelve模块 09 XML模块 10 re模块简介 11 re模块之元字符 第23章 01 re模块之转义字符 02 re模块之分组 03 re模块之方法 04 re模块总结 05 logging模块 06 re模块补充 07 configparse模块 08 hashlib模块 09 计算器作业以及思路 10 模块导入补充 第24章 01 面向对象设计 02 类相关知识 03 对象相关知识 04 类属性增删改查 05 实例属性的增删改查 06 对象与实例属性 07 对象与实例属性补充 08 面向对象作业 第25章 01 上节课回顾 02 静态属性 03 类方法 04 静态方法 05 小结 06 组合 07 继承 08 接口继承与归一化设计 09 继承顺序之mro线性顺序列表 10 在python2的继承顺序是什么 11 在子类调用父类方法 12 super调用父类的方法 13 选择系统作业讲解 第26章 01 学生自主复习 02 分享列表 03 多态 04 封装 05 面向对象概念总结 06 反射 07 反射及动态导入模块 08 类的内置attr属性 09 类内置attr属性补充 10 继承的方完成包装 11 组合的方完成授权 第27章 01 os模块复习 02 上节课复习 03 内置函数补充及getattribute 04 getattribue补充 05 item系列 06 str与repr 07 自定制format 08 slots属性 09 doc属性 10 module和class 11 析构方法 12 call方法 13 迭代器协议 14 迭代器协议实现斐波那契数列 16 描述符答疑 17 描述符优先级 18 软件开发规范 19 pycharm干的好事 第28章 01 上节课复习 02 上下文管理协议 04 异常的构成简单了解 05 描述符应用 08 类的装饰器的基本原理 09 类的装饰器增强版 10 类的装饰器的应用 11 自定制property 12 自定制property流程分析 13 自定制property实现延迟计算功能 14 property补充 15 元类介绍 16 自定义元类 17 函数复习 18 文件操作复习 第29章 01 上节课复习 02 什么是异常处理及异常处理的两种方对比 03 多分支与万能异常 04 异常处理的其他内容 05 什么时候用异常处理 06 什么是socket 07 套接字发展及分类 08 基于tcp协议的套接字编程 09 socket底层工作原理解释 10 tcp三次握手与四次挥手 第30章 01 上节课复习 02 客户端服务端循环收发消息 03 socket收发消息原理剖析 04 服务端循环链接请求来收发消息 05 补充 06 udp套接字 07 recv与recvfrom的区别及基于udp实现ntp服务 08 基于tcp实现远程执行命令 09 基于tcp实现远程执行命令测试结果 10 粘包现象 11 粘包解决方法 第31章 01 上节课复习 02 socketserver实现并发 03 socketserver模块介绍 04 socketserver源码分析tcp版本 05 socketserver源码分析udp版 06 ftp作业要求讲解 07 补充:认证客户端链接合法性 第32章 01 FTP之参数解析与命令分发 02 FTP之逻辑梳理 03 FTP之验证功能 05 FTP之文件上传 06 FTP之断点续传 08 FTP之进度条 09 FTP之cd切换 11 FTP之创建文件夹及MD5校验思路 第33章 01 操作系统历史 02 进程的概念 03 线程的概念 04 线程的调用以及join方法 05 setDaemon方法和继承调用.baiduyun.downloading 05 setDaemon方法和继承调用 第34章 01 上节知识回顾 02 并发并行与同步异步的概念 03 GIL的概念 04 同步锁 05 递归锁 06 同步对象event 07 信号量 08 线程队列 09 生产者消费者模型 10 多进程的调用 第35章 01 进程通信 02 进程池 03 协程 04 事件驱动模型 05 IO模型前戏 06 阻塞IO与非阻塞IO 07 select及触发方 08 select监听多连接 09 select与epoll的实现区别 第36章 01 异步IO 02 selectors模块介绍 03 selectors模块应用 04 作业介绍 第37章 01 selctors实现文件上传与下载 02 html的介绍 03 html文档树的概念 04 meta标签以及一些基本标签 05 img标签和列表标签 06 form表单之input标签 07 通过form向server端发送数据 08 form表单之select标签 09 table标签 第38章 01 css的四种引入方 02 css的四种基本选择器 03 css的组合选择器 04 css的属性选择器 05 css的伪类 06 css的选择器优先级 07 css的背景属性 第39章 01 css的文本属性与边框属性 02 css的列表属性与display属性 03 css的内外边距 04 css的内外边距补充 05 css的float属性 06 css的清除浮动 07 css的定位 08 css的margin定位 第40章 01 抽屉作业之head区域(导航条) 02 抽屉作业之置顶区域 03 抽屉作业之content部分 05 抽屉作业之页码部分 06 抽屉作业之footer部分 第41章 01 JS的历史以及引入方 02 JS的基础规范 03 JS的基本数据类型 04 JS的运算符 05 JS的控制语句与循环 06 JS的循环与异常 07 JS的字符串对象 08 JS的数组对象 09 JS的函数对象 第42章 01 JS的函数作用域 02 JS的window对象之定时器 03 JS的history对象和location对象 04 JS的DOM节点 05 JS的DOM节点 第43章 01 上节知识回顾 02 js之onsubmit事件与组织事件外延 03 DOM节点的增删改查与属性设值 04 正反选练习 05 js练习之二级联动 06 jquery以及jquery对象介绍 07 jquery选择器 08 jquery的查找筛选器 09 jquery练习之左侧菜单 第44章 01 jquery属性操作之html,text,val方法 02 jquery循环方法和attr,prop方法 03 jquery模态对话框与clone的应用 04 jqueryCSS操作之offsets,position以及scrolltop 05 jquery事件绑定与事件委托 06 jquery动画效果 07 jquery扩展与插件 08 jquery扩展补充 09 本周作业轮播图以及思路 第45章 轮播图片css部分 轮播图片js部分 第46章 01 数据库与dbms的概念 02 sql规范 03 数据库操作DDL 04 python s3 day46 mysql的数据类型 05 数据表操作 06 表记录之增删改操作 07 表记录查询之查询 第47章 01 多表查询之连接查询 02 级联删除与set null 03 多表查询之连接查询 04 多表查询之复合查询与子查询 05 mysql之索引 第48章 01 python操作数据库pymysql 02 数据库之事务 03 mysql事务之savepoint 第49章 01 http协议之请求协议 02 http协议之响应协议 03 web框架的概念 04 做一个最简答web框架 05 MVC模和MTV模 06 django的一个简单应用 07 django静态文件之static 08 django的url控制系统 09 django的urlConf补充 第50章 01 django之视图函数的介绍 02 django视图之redirec 03 django模板之变量 04 django模板之过滤器 05 django模板之控制语句if和for循环 06 django模板之标签tag补充 07 django模板之自定义filter和simple_tag 08 django模板之继承标签extend和添加标签include 第51章 01 数据库表与表之间的一对多多对多的关系 02 Django的ORM的概念 03 ORM对单表的增删改操作 04 ORM查询API 05 模糊查询之万能的双下换线 第52章 01 上节知识回顾 02 ORM多表操作之一对多增加记录 03 ORM多表操作之一对多查询之对象查询 04 ORM多表操作之一对多查询之双下划线查询 05 ORM多表操作之多对多添加记录 06 ORM多表操作之多对多查询 07 ORM多表操作之F查询与Q查询 08 ORM的querySet集合对象的特性 第53章 01 admin介绍 02 alex首秀失败 03 自定义admin样 04 admin补充 05 COOKIE介绍 06 COOKIE和SESSION配合使用 第54章 01 今日内容概要 02 Django内容回顾 03 Django请求生命周期之Http请求 04 Django请求生命周期之FBV和CBV 05 Django请求生命周期之CBV扩展 06 瞎扯淡 07 Django请求生命周期之响应内容 08 学员管理示例:数据库设计 09 学员管理示例:班级管理 10 学员管理示例:学员管理 第55章 01 Django的ORM基本操作补充之概要 02 Django的ORM基本操作补充之一对多 03 学员管理示例:编辑学生 04 Django的ORM基本操作补充之多对多 05 学员管理示例:为班级分配老师 06 初识Ajax以及简单应用 07 学员管理示例:Ajax删除学生 08 本节作业以及内容补充 第56章 01 上节内容回顾 02 创建Project以及表结构 03 基于BootStrap和FontAwesome制作页面 04 创建学生信息 05 删除学生信息 第57章 01 上节内容回顾 02 上节bug修复 03 编辑学生信息之前端功能 04 编辑学生信息之后台处理 05 以上内容总结 06 Ajax功能之dataType和traditional 第58章 01 今日内容概要 02 Ajax补充之serialize 03 分页功能介绍 04 分页基础知识 05 Django内置分页 06 扩展Django内置分页 07 自定义分页组件 08 DjangoForm组件初识 第59章 01 Form组件之生成HTML标签 02 Form组件之详解字段 03 Form组件之常用标签示例 04 Form组件之动态绑定数据 第60章 Django序列化共6课 第61章 01 上节内容回顾 02 上传文件 03 制作上传按钮 04 Form组件上传文件 05 上传相关内容梳理 06 Model操作知识提问 07 Model操作概述 08 Model字段 09 Model连表字段参数详解 10 Model自定义多对多第三张表 11 强插一道面试题 12 Model连表操作梳理 13 多对多自关联 14 Model操作补充 15 再插两道JavaScript面试题 16 Model操作之select_related以及prefetch_related 17 Model操作知识梳理以及补充 18 JavaScript两道面试题讲解 第62章 01 今日内容概要 02 创建基本项目 03 XMLHttpRequest对象发送请求 04 XMLHttpRequest对象发送POST请求 05 Iframe伪造Ajax请求 06 Iframe伪造回调函数 07 上述内容整理 08 FormData对象以及Ajax文件上传 09 Iframe文件上传 10 Iframe上传文件 11 JSONP实现AJax跨域 12 内容整理以及CORS简单介绍 第63章 01 项目以及学习介绍 02 企业官网示例功能介绍 03 企业官网示例以及数据库表结构 04 企业官网示例作业要求 05 Toando源码基本基本介绍 第64章 01 组合搜索 02 瀑布流 03 瀑布流作业 第65章 01 今日内容概要 02 瀑布流作业讲解 03 保障系统需求分析 04 保障系统数据库设计 05 保障系统目录结构规定 06 阶段任务安排 第66章 01 保障系统主页功能讲解 02 保障系统主页分类和分页的实现 03 阶段作业:保障系统登录注册 第67章 01 保障系统之登录注册功能讲解 02 保障系统之网站验证码 03 保障系统之一个月免登陆 04 任务安排 第68章 01 保障系统之个人知识库主页 02 保障系统之个人知识库内容筛选 03 保障系统之文章最终页 04 保障系统之KindEditor基本使用 05 保障系统之下节预告 06 今日作业以及下节预告 第69章 01 后台管理功能介绍 02 后台管理页面布局 03 今日作业以及下节预告 第70章 01 后台管理之创建报障单 02 后台管理之处理报障单 03 后台管理之画图流程 04 后台管理之Highchart统计保障单 第71章 01 权限管理要求 02 权限管理数据库表设计 03 填充权限数据 04 作业:获取权限以及菜单信息 05 作业思路讲解 06 权限管理之获取用户权限信息 07 权限管理之获取用户菜单信息 08 权限管理之用户权限挂靠到菜单上 09 权限管理之处理菜单等级关系 第72章 01 上节内容概要以及标记应该显示的菜单 02 权限管理之递归生成多级菜单 03 权限管理之标记当前以及激活菜单 04 权限管理之基本使用 05 权限管理之封装权限组件 06 下节预告 第73章 01 CMDB项目介绍 02 CMDB开发背景 03 CMDB开发目的 04 CMDB资产采集方之agent 05 CMDB资产采集方之ssh 06 CMDB资产采集方之saltstack 07 CMDB资产采集方之puppet 08 CMDB资产采集方比较 09 CMDB资产采集内容梳理 10 CMDB资产采集功能实现之agent 11 CMDB资产采集功能实现之ssh 12 CMDB资产采集功能实现之saltstack 13 CMDB资产采集插件开发 14 内容回顾之面向对象继承 15 作业:基于配置文件加载插件 第74章 01 CMDB项目上节作业讲解 02 CMDB项目采集资产数据 03 知识拾遗之线程进程池 04 CMDB项目采集资产之日志记录 05 自定义JSON序列化类型 06 本节作业 第75章 01 基于requests模块汇报资产数据 02 基于requests模块汇报API验证 03 CMDB项目示例之API验证流程 04 CMDB项目之数据库表结构 05 CMDB项目之资产汇报并持久化 06 CMDB项目之持久化资产流程 07 本周作业 第76章 01 CMDB项目CURD组件之配置文件构造 02 CMDB项目CURD组件之神奇的单@符号 03 CMDB项目CURD组件之神奇的双@符号 04 CMDB项目CURD组件之自定义td属性 05 下节内容预习 第77章 01 CMDB项目CURD组件之进入编辑模 02 CMDB项目CURD组件之全选取消反选和编辑模 03 CMDB项目CURD组件之内容截图 04 CMDB项目CURD组件之进入编辑模详细 05 CMDB项目CURD组件之属性应用神奇的单@符号 06 CMDB项目CURD组件之退出编辑模 07 CMDB项目CURD组件之更新数据 08 CMDB项目CURD组件之基于jQuery扩展封装组件 09 CMDB项目CURD组件之10分钟搞定页面基本操作 10 CMDB项目CURD组件之分页功能 11 CMDB项目CURD组件之搜索功能介绍 12 CMDB项目总结 第78章 01 剩余项目概览 02 CRM项目需求分析 04 CRM项目需求分析及架构设计 05 CRM项目表结构设计 第79章 01 CRM项目实战-前端页面布局 02 CRM项目实战-登录页面开发 04 CRM项目实战-动态菜单设计 05 CRM项目实战-kingadmin开发设计 06 CRM项目实战-kingadmin自动发现及注册功能开发 07 CRM项目实战-kingadmin model obj list页面开发 08 CRM项目实战-kingadmin 根据list_display配置生成数据列表 09 CRM项目实战-kingadmin 多条件过滤功能开发 第80章 01 课前鸡汤 02 分页功能开发 03 分页功能优化 04 排序功能开发 05 分页 排序 筛选组合使用 06 搜索功能开发 第81章 01 CRM项目实战 - 动态modelform的实现 02 CRM项目实战 - 动态modelform 增加自定义样 03 CRM项目实战 - 实现任意表的增删改查 04 CRM项目实战 - 只读字段的处理 05 CRM项目实战 - filter_horizontal的实现 第82章 01 CRM项目实战 - kingadmin m2m filter_horizontal优化 02 CRM项目实战 - kingadmin 对象删除功能开发 03 CRM项目实战 - kingadmin+admin+action功能开发 04 CRM项目实战 - csrf+token验证原理 第83章 01 CRM项目-kingadmin批量删除 02 CRM项目-学员报名流程开发 03 CRM项目-本次作业需求 第84章 01 SSO介绍 02 用户自定义认证 03 万能通用权限框架设计 04 万能通用权限框架设计-自定义权限钩子实现 第85章 01 堡垒机项目实战-需求讨论 02 堡垒机项目实战-表结构设计 第86章 01 堡垒机项目实战-用户交互程序开发 02 堡垒机项目实战-通过paramiko记录ssh会话记录 03 堡垒机项目实战-把parmaiko代码嵌入用户交互程序 04 堡垒机项目实战-在数据库里记录用户会话数据 第87章 01 堡垒机项目实战-前端模板的选择 02 堡垒机项目实战-web ssh的使用 03 堡垒机项目实战-批量任务的思路 04 堡垒机项目实战-批量任务的前端页面开发 第88章 01 堡垒机实战-批量命令后端开发 02 堡垒机实战-批量命令前端获取执行结果 03 堡垒机实战-批量文件分发 第89章 01 版本管理工具介绍 02 git基本使用 03 github使用 04 git 分支开发流程 05 restful规范介绍 06 restful api设计指南 第90章 01 rabbitmq 消息安全接收 02 rabbitmq 消息持久化 03 rabbitmq 消息订阅发布 04 rabbitmq 消息组播 05 rabbitmq 消息RPC 第91章 01 阶段课程安排介绍 02 爬虫介绍 03 初识爬虫之采集汽车资讯信息 04 requests和beautfulsoup模块基本使用 05 示例:自动登录抽屉新热榜 06 requests模块详细介绍 第92章 01 BeautifulSoup模块详细介绍 02 示例:自动登录知乎 03 示例:自动登录博客园 04 作业之开发Web微信 第93章 01 开发Web微信前戏 02 Web微信流程介绍 03 Web微信之用户扫码 04 Web微信之用户信息初始化 05 Web微信开发总结 第94章 01 Web微信之获取用户初始化信息并展示 02 Web微信之发送微信消息 03 Web微信之获取用户消息 04 Web微信开发总结 第95章 01 多线程实现并发请求 02 多进程实现并发请求 03 异步IO模块的使用 04 自定义异步IO模块前戏 05 自定义异步IO模块开发 06 自定义异步IO模块开发 第96章 01 Scrapy爬虫介绍 02 抽屉示例:初识Scrapy并获取新闻标题 03 抽屉示例:递归获取所有页码 04 抽屉示例:基于pipeline实现标题和URL持久化 第97章 01 Scrapy上节回顾 02 去除重复URL 03 pipeline补充 04 自动登录抽屉并点赞 05 scrapy框架扩展 06 配置文件 第98章 01 Scrapy配置之自动限速以及缓存 02 Scrapy之默认代理以及扩展代理 03 Scrapy之自定义Https证书 04 Scrapy配置之下载间件 05 Scrapy配置之爬虫间件 06 Scrapy配置之自定义scrapy命令 07 Scrapy源码流程简述 第99章 01 今日内容概要 02 Scrapy源码剖析前戏之Twisted使用 03 Scrapy源码剖析之自定义Low版框架 04 Scrapy源码剖析之自定义TinyScrapy框架 05 Scrapy源码剖析流程解析 第100章 01 Tornado学习概要 02 Tornado基本操作 03 Tornado自定义Session知识前戏 04 Tornado自定义Session 第101章 01 WebSocket介绍 02 WebSocket握手过程分析 03 基于Python实现WebSocket握手过程 04 位运算补充 05 WebSocket数据解析过程 06 基于Tornado的WebSocket实现聊天室 第102章 01 今日内容复习目标 02 异步非阻塞框架介绍 03 Tornado异步非阻塞功能使用 04 自定义Web框架(同步) 05 自定义Web框架支持同步和异步非阻塞 06 复习计划 第103章 01 缓存数据库介绍 02 redis string操作 03 redis hash 操作 04 redis list操作 05 redis 集合操作 06 redis 其他常用命令 07 redis 发布订阅 08 celery介绍和基本使用 09 celery在项目使用 10 celery定时任务 11 celery在项目使用 12 celery 在django实现定时任务 第104章 就业指导 01 简历制作 02 如何面试 03 最后的鸡汤 04 Nginx+uWSGI+Django部署
在C#SendMessage和PostMessage的参数传递 在C#可以使用Window API提供的SendMessage和PostMessage来传递参数。两者的区别简单介绍下:返回值的不同,我们先看一下 MSDN 里的声明: LRESULT SendMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ); BOOL PostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ); 其 4 个参数的意义是一样的,返回值类型不同(其实从数据上看他们一样是一个 32 位的数,只是意义不一样),LRESULT 表示的是消息被处理后的返回值,BOOL 表示的是消息是不是 Post 成功。 2、PostMessage 是异步的,SendMessage 是同步的。 PostMessage 只把消息放入队列,不管消息是否被处理就返回,消息可能不被处理;而 SendMessage 等待消息被处理完了之后才返回,如果消息不被处理,发送消息的线程将一直被阻塞。 3、如果在同一个线程内,SendMessage 发送消息时,由 USER32.DLL 模块调用目标窗口的消息处理程序,并将结果返回。SendMessage 在同一线程发送消息并不入线程消息队列。PostMessage 发送消息时,消息要先放入线程的消息队列,然后通过消息循环分派到目标窗口(DispatchMessage)。 如果在不同线程内,SendMessage 发送消息到目标窗口所属线程的消息队列,然后发送消息的线程在 USER32.DLL 模块内监视和等待消息处理,直到目标窗口处理完返回。SendMessage 在返回前还做了很多工作,比如,响应别的线程向它 SendMessage。Post 到别的线程时,最好用 PostThreadMessage 代替 PostMessage,PostMessage 的 hWnd 参数可以是 NULL,等效于 PostThreadMessage + GetCurrentThreadId。Post WM_QUIT 时,应使用 PostQuitMessage 代替。 4、系统只整编(marshal)系统消息(0 到 WM_USER 之间的消息),发送用户消息(WM_USER 以上)到别的进程时,需要自己做整编。 用 PostMessage、SendNotifyMessage、SendMessageCallback 等异步函数发送系统消息时,参数里不可以使用指针,因为发送者并不等待消息的处理就返回,接受者还没处理指针就已经被释放了。 5、在 Windows 2000/XP 里,每个消息队列最多只能存放 10,000 个 Post 的消息,超过的还没被处理的将不会被处理,直接丢掉。这个值可以改得更大:[HKEY_LOCAL_MACHINE/SOFTWARE/ Microsoft/Windows NT/CurrentVersion/Windows] USERPostMessageLimit,最小可以是 4000。 PostMessage只负责将消息放到消息队列,不确定何时及是否处理 SendMessage要等到受到消息处理的返回码(DWord类型)后才继续 PostMessage执行后马上返回 SendMessage必须等到消息被处理后才会返回。
线程的状态以及各状态之间的转换详解.mp4 线程的初始化,断以及其源码讲解.mp4 多种创建线程的方案例演示(一)带返回值的方.mp4 多种创建线程的方案例演示(二)使用线程池.mp4 Spring对并发的支持:Spring的异步任务.mp4 使用jdk8提供的lambda进行并行计算.mp4 了解多线程所带来的安全风险.mp4 从线程的优先级看饥饿问题.mp4 从Java字节码的角度看线程安全性问题.mp4 synchronized保证线程安全的原理(理论层面).mp4 synchronized保证线程安全的原理(jvm层面).mp4 单例问题与线程安全性深入解析.mp4 理解自旋锁,死锁与重入锁.mp4 深入理解volatile原理与使用.mp4 JDK5提供的原子类的操作以及实现原理.mp4 Lock接口认识与使用.mp4 手动实现一个可重入锁.mp4 AbstractQueuedSynchronizer(AQS)详解.mp4 使用AQS重写自己的锁.mp4 重入锁原理与演示.mp4 读写锁认识与原理.mp4 细读ReentrantReadWriteLock源码.mp4 ReentrantReadWriteLock锁降级详解.mp4 线程安全性问题简单总结.mp4 线程之间的通信之wait notify.mp4 通过生产者消费者模型理解等待唤醒机制.mp4 Condition的使用及原理解析.mp4 使用Condition重写waitnotify案例并实现一个有界队列.mp4 深入解析Condition源码.mp4 实战:简易数据连接池.mp4 线程之间通信之join应用与实现原理剖析.mp4 ThreadLocal 使用及实现原理.mp4 并发工具类CountDownLatch详解.mp4 并发工具类CyclicBarrier 详解.mp4 并发工具类Semaphore详解.mp4 并发工具类Exchanger详解.mp4 CountDownLatch,CyclicBarrier,Semaphore源码解析.mp4 提前完成任务之FutureTask使用.mp4 Future设计模实现实现类似于JDK提供的Future).mp4 Future源码解读.mp4 ForkJoin框架详解.mp4 同步容器与并发容器.mp4 并发容器CopyOnWriteArrayList原理与使用.mp4 并发容器ConcurrentLinkedQueue原理与使用.mp4 Java阻塞队列原理与使用.mp4 实战:简单实现消息队列.mp4 并发容器ConcurrentHashMap原理与使用.mp4 线程池的原理与使用.mp4 Executor框架详解.mp4 实战:简易web服务器(一).mp4 实战:简易web服务器(二).mp4 JDK8的新增原子操作类LongAddr原理与使用.mp4 JDK8新增锁StampedLock详解.mp4 重排序问题.mp4 happens-before简单概述.mp4 锁的内存语义.mp4 volatile内存语义.mp4 final域的内存语义.mp4 实战:问题定位.mp4
### 回答1: 在Java,如果希望在不影响主线程运行的情况下处理异步方法的返回值,则可以使用以下方法之一: 1. 使用`Executor`和`Future`:可以使用`Executor`框架的`ExecutorService`来创建一个线程池,然后使用`submit`方法提交一个异步任务,该方法将返回一个`Future`对象,可以使用`get`方法获取异步任务的返回值。例如: ``` ExecutorService executor = Executors.newFixedThreadPool(10); Future<Integer> result = executor.submit(new Callable<Integer>() { public Integer call() throws Exception { // 异步处理的代码 return 123; } }); // 在主线程处理异步方法的返回值 int value = result.get(); ``` 2. 使用`CompletableFuture`:Java 8引入了`CompletableFuture`类,它是对`Future`的增强版,支持将多个异步任务串联起来,并提供了更丰富的回调机制。例如: ``` CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { // 异步处理的代码 return 123; }); // 在主线程处理异步方法的返回值 int value = future.get(); ``` 3. 使用`CompletionStage`:Java 9引入了`CompletionStage`接口,它也支持将多个异步任务串联起来,但是和`CompletableFuture`相比,它更加灵活,可以使用不同的实现类来实现异步流程。例如: ``` CompletionStage<Integer> stage = CompletableFuture.supply ### 回答2: 在Java,要处理异步方法的返回值而不影响主线程的运行流程,可以使用线程池和Future对象来实现。 首先,创建一个线程池来管理线程的执行。可以使用Executors类的静态工厂方法创建一个线程池,例如: ``` ExecutorService executor = Executors.newFixedThreadPool(1); ``` 然后,将异步方法的调用封装在Callable对象,使用submit方法将Callable对象提交给线程池执行,并返回一个Future对象,例如: ``` Callable<String> asyncMethod = () -> { // 异步方法的逻辑 return "异步方法的返回值"; }; Future<String> future = executor.submit(asyncMethod); ``` 接下来,在主线程可以继续执行其他任务,而不用等待异步方法的返回结果。如果需要获取异步方法的返回值,可以使用Future对象的get方法,该方法会阻塞主线程直到异步方法执行完成并返回结果,例如: ``` String result = future.get(); ``` 需要注意的是,get方法也可以设置超时时间,当异步方法执行时间超过超时时间时,get方法会抛出TimeoutException异常。 最后,在不需要线程池时,可以使用shutdown方法关闭线程池,例如: ``` executor.shutdown(); ``` 通过使用线程池和Future对象,可以实现在Java处理异步方法的返回值而不影响主线程的运行流程。 ### 回答3: 在Java,要处理异步方法的返回值而不影响主线程运行的流程,可以使用Future和CompletableFuture机制。 Future是Java标准库提供的异步编程机制之一,通过Future实例可以获取异步任务的返回结果。在主线程,可以使用Future.get()方法来等待异步任务的完成,并获取异步任务的返回结果。这样,主线程会在此处阻塞,直到异步任务完成。 但是,如果主线程需要继续执行其他任务,而不是阻塞等待异步任务的完成,可以使用CompletableFuture。CompletableFuture是在Java 8引入的,它提供了更多便利的方法用于异步编程。通过CompletableFuture,我们可以更加灵活地处理需要异步执行的任务。 使用CompletableFuture,我们可以通过thenApply()、thenAccept()、thenRun()等方法将异步任务与主线程的后续操作串联起来。这样,异步任务的结果会在主线程的某个位置被处理,而主线程可以继续执行其他任务。同时,我们还可以通过调用CompletableFuture.join()方法,使主线程等待异步任务的完成。 总结起来,Java可以通过Future和CompletableFuture来处理异步方法的返回值,而不影响主线程的运行流程。使用Future时,主线程会在调用get()方法处阻塞等待,而使用CompletableFuture时,主线程可以继续执行其他任务,并在合适的位置处理异步任务的返回结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想养一只!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值