SpringBoot 并发编程学习历程(绝对的干货)

news2024/12/23 11:47:44

如果一个项目总用单线程来跑,难免会遇到一些性能问题,所以再开发中,我们应该尽量适量的使用多线程(在保证线程安全的情况下)。

本教程大概目录:

1.模拟单线程情节
2.用Callable实现 并发编程
3.用DeferedResult实现异步处理

模拟单线程情节

/**
 * Created by Fant.J.
 */
@RestController
@Slf4j
public class AsyncController {/** * 单线程测试 * @return * @throws InterruptedException */@RequestMapping("/order")public String order() throws InterruptedException {log.info("主线程开始");Thread.sleep(1000);log.info("主线程返回");return "success";}
} 

我们把线程休息一秒当作模拟处理业务所花费的时间。很明显能看出来,这是个单线程。

nio-8080-exec-1表示主线程的线程1。

用Callable实现 并发编程

 /** * 用Callable实现异步 * @return * @throws InterruptedException */@RequestMapping("/orderAsync")public Callable orderAsync() throws InterruptedException {log.info("主线程开始");Callable result = new Callable() {@Overridepublic Object call() throws Exception {log.info("副线程开始");Thread.sleep(1000);log.info("副线程返回");return "success";}};log.info("主线程返回");return result;} 

我们可以看到,主线程的开始和返回(结束处理)是首先执行的,然后副线程才执行真正的业务处理。说明主线程在这里的作用是调用(唤醒)子线程,子线程处理完会返回一个Object对象,然后返回给用户。

这样虽然实现了并发处理,但是有一个问题,就是主线程和副线程没有做到完全分离,毕竟是一个嵌套进去的副线程。

所以为了优化我们的实现,我在这里模拟 消息中间件 来实现主线程副线程的完全分离。

用DeferedResult实现异步处理

因为本章主要讲的是并发编程原理,所以这里我们不用现成的消息队列来搞,我们模拟一个消息队列来处理。

MockQueue .java
/**
 * 模拟消息队列 类
 * Created by Fant.J.
 */
@Component
@Slf4j
public class MockQueue {//下单消息private String placeOrder;//订单完成消息private String completeOrder;public String getPlaceOrder() {return placeOrder;}public void setPlaceOrder(String placeOrder) throws InterruptedException {new Thread(()->{log.info("接到下单请求"+placeOrder);//模拟处理try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//给completeOrder赋值this.completeOrder = placeOrder;log.info("下单请求处理完毕"+placeOrder);}).start();}public String getCompleteOrder() {return completeOrder;}public void setCompleteOrder(String completeOrder) {this.completeOrder = completeOrder;}
} 

注意再setPlaceOrder(String placeOrder)方法里,我创建了一个新的线程来处理接单的操作(为什么要建立新线程,怕主线程在这挂起,此段逻辑也没有线程安全问题,况且异步处理更快)。传进来的参数是个 订单号 ,经过1s的处理成功后,把订单号传给completeOrder 字段,说明用户下单成功,我在下面付controller调用该方法的代码

 //注入模拟消息队列类@Autowiredprivate MockQueue mockQueue;@Autowiredprivate DeferredResultHolder deferredResultHolder;....@RequestMapping("/orderMockQueue")public DeferredResult orderQueue() throws InterruptedException {log.info("主线程开始");//随机生成8位数String orderNumber = RandomStringUtils.randomNumeric(8);mockQueue.setPlaceOrder(orderNumber);DeferredResult result = new DeferredResult();deferredResultHolder.getMap().put(orderNumber,result);Thread.sleep(1000);log.info("主线程返回");return result;} 

好了,然后我们还需要一个中介类来存放订单号和处理结果。为什么需要这么一个类呢,因为我们之前说过要实现主线程和副线程分离,所以需要一个中介来存放处理信息(比如:这个订单号信息,和处理结果信息),我们判断处理结果是否为空就知道该副线程执行了没有。所以我们写一个中介类DeferredResultHolder 。

######DeferredResultHolder .java

 /**
 *订单处理情况 中介/持有者
 * Created by Fant.J.
 */
@Component
public class DeferredResultHolder {/** * String: 订单号 * DeferredResult:处理结果 */private Map<String,DeferredResult> map = new HashMap<>();public Map<String, DeferredResult> getMap() {return map;}public void setMap(Map<String, DeferredResult> map) {this.map = map;}
} 

在重复一次-.-,为什么需要这么一个类呢,因为我们之前说过要实现主线程和副线程分离,所以需要一个中介来存放处理信息(比如:这个订单号信息,和处理结果信息),一个订单肯定要对应一个结果。不然岂不是乱了套。

DeferredResult是用来放处理结果的对象。

好了,那新问题又来了,我们怎么去判断订单处理成功了没有,我们此时就需要写一个监听器,过100毫秒监听一次MockQueue类中的completeOrder中是否有值,如果有值,那么这个订单就需要被处理。我们写一个监听器。

QueueListener .java
 /**
 * Queue监听器
 * Created by Fant.J.
 */
@Component
@Slf4j
public class QueueListener implements ApplicationListener<ContextRefreshedEvent>{@Autowiredprivate MockQueue mockQueue;@Autowiredprivate DeferredResultHolder deferredResultHolder;@Overridepublic void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {new Thread(()->{while(true){//判断CompleteOrder字段是否是空if (StringUtils.isNotBlank(mockQueue.getCompleteOrder())){String orderNumber = mockQueue.getCompleteOrder();deferredResultHolder.getMap().get(orderNumber).setResult("place order success");log.info("返回订单处理结果");//将CompleteOrder设为空,表示处理成功mockQueue.setCompleteOrder(null);}else {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}
} 

我们可以看到一共有三个不同的线程来处理。



分割线后,我再给大家带来一批干货,自定义线程池 https://www.jianshu.com/p/832f2b162450

学完这个后,再看下面的。。

我们前面的代码中,有两部分有用new Thread()来创建线程,我们有自己的线程池后,就可以用线程池来分配线程任务了,我在自定义线程里有讲,我用的是第二种配置方法(用@Async注解来给线程 )。 修改如下:

 @Asyncpublic void setPlaceOrder(String placeOrder) throws InterruptedException {log.info("接到下单请求"+placeOrder);//模拟处理try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//给completeOrder赋值this.completeOrder = placeOrder;log.info("下单请求处理完毕"+placeOrder);} 

我们看看效果:

圈红圈的就是我们自己定义的线程池里分配的线程。

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

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

相关文章

高并发系统设计 --基于bitmap的用户签到

业务需求分析 一般像微博&#xff0c;各种社交软件&#xff0c;游戏等APP&#xff0c;都会有一个签到功能&#xff0c;连续签到多少天&#xff0c;送什么东西&#xff0c;比如&#xff1a; 签到1天送10积分&#xff0c;连续签到2天送20积分&#xff0c;3天送30积分&#xff0…

Qt之QDrag的使用(含源码+注释)

一、效果示例图 提示&#xff1a;主控件&#xff08;CDragTest界面&#xff0c;UI中中包含CWidget界面&#xff09;&#xff1b;子控件&#xff08;CWidget界面&#xff0c;在CDragTest界面添加&#xff09; 提示&#xff1a;源码中拖拽数据设置的文本不同&#xff0c;是博主准…

【ONE·C || 分支循环】

总言 C语言&#xff1a;分支循环。 文章目录总言1、分支语句1.1、if语句1.1.1、基本格式1.1.2、逻辑真假与悬空else1.1.3、练习1.2、switch语句1.2.1、基本格式&#xff1a;break、case、default1.2.2、练习&#xff1a;switch语句嵌套2、循环语句2.1、while循环2.1.1、基本格式…

uniapp实现界面可任意拖动小图标

uniapp实现界面可任意拖动小图标一、问题&#xff1a;二、解决步骤2.1 根据uni-app官方提供的案例&#xff0c;创建组件2.2 在需要的界面引入组件使用额外注意一、问题&#xff1a; 例如购物车小图标可任意拖动 二、解决步骤 2.1 根据uni-app官方提供的案例&#xff0c;创建…

Kubernetes教程(二)---集群网络之 Flannel 核心原理

来自&#xff1a;指月 https://www.lixueduan.com 原文&#xff1a;https://www.lixueduan.com/posts/kubernetes/02-cluster-network/ 本文主要记录了 Kubernetes 集群网络方案之 Flannel 核心原理详解&#xff0c;包括其隧道方案中的两种&#xff1a;UDP 实现和 VXLAN 实现…

Mysql之增删改查

这里的增删改查主要是对应表中的数据&#xff0c;不像前一篇那个列类型&#xff0c;耳机具体的哪一条数据 Insert 其实我们前面都用过好多次了 比如下面那个 可以 关于那个表名后面加不加&#xff08;列类型&#xff09;&#xff0c;下面有解释 INSERT INTO shanpin VALUES…

关于yolov8一些训练的情况

U神出品了最新的yolov8&#xff0c;从公开的参数量来看确实很优秀&#xff01;&#xff01;&#xff01;&#xff01;比如下图得一些指标&#xff1a; 可以看到s模型640得map已经达到了44.9&#xff0c;v8n得map也已经达到了37.3&#xff0c;很强了&#xff0c;但是实际上是怎么…

Python爬虫之Scrapy框架系列(3)——项目实战【某瓣top250电影信息获取】

目录&#xff1a;1. 某瓣电影top250首页电影信息的获取&#xff01;1.创建项目&#xff1a;2.创建爬虫文件&#xff1a;3.运行爬虫文件&#xff1a;4.设置请求头&#xff1a;5.获取到电影名字&#xff1a;5.1 使用shell交互式平台&#xff1a;5.1.1 首先&#xff1a;打开我们的…

239页10万字“联、管、用”三位一体雪亮工程整体建设方案

【版权声明】本资料来源网络&#xff0c;知识分享&#xff0c;仅供个人学习&#xff0c;请勿商用。【侵删致歉】如有侵权请联系小编&#xff0c;将在收到信息后第一时间删除&#xff01;完整资料领取见文末&#xff0c;部分资料内容&#xff1a; 目录 1、 项目概述 1.1 项目背…

用R语言绘制泰勒级数的逼近过程

文章目录泰勒级数是如何被发现的用图像理解Taylor级数的逼近过程前情提要 R语言微积分极限π,e,γ\pi, e, \gammaπ,e,γ洛必达法则连续性和导数数值导数差商与牛顿插值方向导数 泰勒级数是如何被发现的 如果我是泰勒&#xff0c;我会把思考的起点建立在这样的一个等式上 f(n…

Windows10电脑重装系统详细步骤(纯净版)

目录 前言&#xff1a; 一、准备工作 二、下载pe工具 三、下载系统镜像ISO文件 获取方式一 获取方式二 获取方式三 四、进入pe系统 1.检查以上的准备工作是否完成 2.然后拔出来u盘插入要重装的电脑上面 3.然后按电源键开机&#xff08;不能点击重启&#xff01;&…

【Git 从入门到精通】使用Git将本地代码推送到Github

文章目录一、创建远程库二、Git操作远程库1.推送代码2.克隆代码3.拉取代码4.Pull request5.常用命令总结一、创建远程库 打开github.com&#xff0c;点击右上角加号&#xff0c;点击第一个选项。 填写库的基本信息&#xff0c;如果你想代码开源就选择public&#xff0c;否则就…

开发模型和测试模型

开发模型瀑布模型特点&#xff1a;线性结构&#xff0c;每个阶段只执行一次&#xff0c;必须完成上一个才能执行下一个。是其他模型的基础框架缺点&#xff1a;测试后置&#xff0c;1&#xff09;前面各个阶段的遗留的风险推迟到测试阶段才被发现&#xff0c;导致项目大面积返工…

【7】SCI易中期刊推荐——图像处理领域(中科院4区)

🚀🚀🚀NEW!!!SCI易中期刊推荐栏目来啦 ~ 📚🍀 SCI即《科学引文索引》(Science Citation Index, SCI),是1961年由美国科学信息研究所(Institute for Scientific Information, ISI)创办的文献检索工具,创始人是美国著名情报专家尤金加菲尔德(Eugene Garfield…

【LGR-(-17)】洛谷入门赛 #8个人思考

T306713 Hello, 2023 题目背景 Goodbye, 2022 Hello, 2023 题目描述 某 E 在 2022 年的幸运数字是 xxx&#xff0c;这个数可能是正的&#xff0c;也可能是负的。 某 E 想要知道 xmod2023x \bmod 2023xmod2023 的值。其中&#xff0c;mod\bmodmod 是取模操作。也就是说&am…

数据结构:线性表的顺序表示和实现

在实际应用程序中涉及的线性表的基本操作都需要针对线性表的具体存储结构加以实现。线性表可以有两种存储表示方法:顺序存储表示和链式存储表示。下面我们先说说顺序存储表示。 1、顺序表——线性表的顺序存储表示 在计算机中表示线性表的最简单的方法是用一组地址连续的存储…

Linux:自动化构建工具make/Makefile

文章目录一.前言二.Makefile如何写入/make命令使用2.1清楚依赖关系和依赖方法2.2删除文件2.3Makefile中的关键字.PHONY2.4一个小补充一.前言 在此之前我们已经可以用vim编写代码和用gcc编译代码。但是如果现在要写一个大型项目&#xff0c;一下子写了很多源文件&#xff0c;在…

C. Zero Path(DP)

Problem - 1695C - Codeforces 给你一个有n行和m列的网格。我们用(i,j)表示第i(1≤i≤n)行和第j(1≤j≤m)列的方格&#xff0c;用aij表示那里的数字。所有的数字都等于1或等于-1。 你从方格&#xff08;1,1&#xff09;开始&#xff0c;每次可以向下或向右移动一个方格。最后&…

基于结点的数据结构——链表(单链表双向循环链表)| 附完整源码 | C语言版

本章内容 1.什么是链表 2.链表常见几种形式 3.无头单向非循环链表的实现 3.1结点结构的定义 3.2函数接口的实现 3.2.1尾插 3.2.2尾删 4. 带头双向循环链表的实现 4.1结点结构的定义 4.2函数接口的实现 5.两种链表的差异 ①尾插与尾删的时间复杂度 ②头插与头删的时…

Ai 作图 stable-diffusion-webui prompt

文章参考了 prompt指导手册 &#xff1a; https://strikingloo.github.io/stable-diffusion-vs-dalle-2 https://prompthero.com/stable-diffusion-prompt-guide 一般来说&#xff0c;最好的稳定扩散提示会有这样的形式&#xff1a; “ [主要主题]的[图片类型] &#xff0…