JUC高并发编程(一)之请求合并案例
1.背景
在做活动或者抢购场景,系统查询的请求并发量非常高
如果并发的访问数据库,会给数据库带来很大的压力,
这时候我们可以考虑将多个查询请求合并成一个查询请求返回给客户端,
比如:根据id查询爆款产品
并发10000次
3000次查询 id=1的产品
4000次查询id=2的产品
2000次查询id=3的产品
1000次查询id=4的产品
如果请求不合并,将到数据库查询10000次,
如果采用请求合并,只需到数据库查询1次,共查询出4个产品,然后按照id把结果给每一个请求,
这样大大降低了数据库的压力
使用到的关于juc相关技术:
1.
2.
3.
2.代码
控制层代码:
/**
* 查询订单
*
* @return
*/
@RequestMapping("/api/product")
public Object product(String id) throws ExecutionException, InterruptedException {
// 为了便于分析,设置一个线程号
Thread.currentThread().setName("thread-" + id);
Map<String, Object> map = productServiceImpl.queryList(id);
// 模拟随机耗时
ThreadUtil.sleepRandom();
return map;
}
业务实现层代码:
package com.ldp.jucproject.service.impl; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.*; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 11/06 12:51
* @description
*/
@Service
public class ProductServiceImpl {
/**
* 请求类,code为查询的共同特征,例如查询商品,通过不同id的来区分
* CompletableFuture将处理结果返回
*/
class Request {
String code;
CompletableFuture completableFuture;
} /**
* 存放请求的队列
*/
LinkedBlockingQueue<Request> queue = new LinkedBlockingQueue(200); @PostConstruct
public void init() {
// 建立定时执行的线程
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
scheduledExecutorService.scheduleAtFixedRate(() -> {
int size = queue.size();
//如果队列没数据,表示这段时间没有请求,直接返回
if (size == 0) {
return;
}
List<Request> list = new ArrayList<>();
System.out.println("合并请求数:" + size);
//将队列的请求消费到一个集合保存
for (int i = 0; i < size; i++) {
Request poll = queue.poll();
if (poll == null) {
System.out.println("无请求对象!");
break;
}
list.add(poll);
}
//拿到我们需要去数据库查询的特征,保存为集合
List<String> listParam = new ArrayList<>();
for (Request request : list) {
listParam.add(request.code);
}
//将参数传入service处理
Map<String, HashMap<String, Object>> response = queryByCodeBatch(listParam);
//将处理结果返回各自的请求
for (Request request : list) {
Map<String, Object> result = response.get(request.code);
//completableFuture.complete方法完成赋值,这一步执行完毕,阻塞的请求可以继续执行了
request.completableFuture.complete(result);
}
}, 0, 30, TimeUnit.MILLISECONDS);
} /**
* 模拟从数据库查询
*
* @param codes
* @return
*/
public Map<String, HashMap<String, Object>> queryByCodeBatch(List<String> codes) {
Map<String, HashMap<String, Object>> result = new HashMap();
for (String code : codes) {
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("productCode", new Random().nextInt(999999999));
hashMap.put("code", code);
hashMap.put("productNome", "苹果-" + new Random().nextInt(5));
hashMap.put("price", new Random().nextInt(100));
result.put(code, hashMap);
}
return result;
} public Map<String, Object> queryList(String code) throws ExecutionException, InterruptedException {
Request request = new Request();
request.code = code;
CompletableFuture<Map<String, Object>> future = new CompletableFuture<>();
request.completableFuture = future;
//将对象传入队列
boolean offer = queue.offer(request);
if (!offer) {
// 放入队列失败,说明队列满了,返回系统忙
Map<String, Object> result = new HashMap<>();
result.put("code", "9999");
result.put("message", "系统忙!");
return result;
}
//如果这时候没完成赋值,那么就会阻塞,知道能够拿到值
return future.get();
}
}
3.测试
模拟请求:
@Test
void product() throws InterruptedException {
// 并发请求数
int num = 1000;
CountDownLatch countDownLatch = new CountDownLatch(num);
for (int i = 1; i <= num; i++) {
// 计数器减一
countDownLatch.countDown();
Integer id = i;
new Thread(() -> {
try {
String url = "http://localhost:8001/api/product?id=" + id;
// 等待计数器归零,归零前都是处于阻塞状态
System.out.println("待查询订单号=" + id);
countDownLatch.await();
HttpRequest request = HttpUtil.createGet(url);
String response = request.execute().body();
System.out.println("response=" + response);
} catch (Exception e) {
log.error("模拟并发出错:{}", e);
}
}).start();
}
// 避免线程终止
Thread.sleep(90 * 1000);
}
4.完美!
JUC高并发编程(一)之请求合并案例的更多相关文章
- 《深入理解高并发编程:JDK核心技术》-冰河新书上市
大家好,我是冰河~~ 废话说多了没用,并发编程技术一直是初级程序员进阶高级工程师的前提条件,也是成为大厂程序员的必备技能,更是突破自身技术瓶颈的必经之路. 2022年6月我出版了"冰河技术丛 ...
- Java 面试知识点解析(二)——高并发编程篇
前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...
- 关于Java高并发编程你需要知道的“升段攻略”
关于Java高并发编程你需要知道的"升段攻略" 基础 Thread对象调用start()方法包含的步骤 通过jvm告诉操作系统创建Thread 操作系统开辟内存并使用Windows ...
- Java高并发编程基础三大利器之CountDownLatch
引言 上一篇文章我们介绍了AQS的信号量Semaphore<Java高并发编程基础三大利器之Semaphore>,接下来应该轮到CountDownLatch了. 什么是CountDownL ...
- java线程高并发编程
java线程具体解释及高并发编程庖丁解牛 线程概述: 祖宗: 说起java高并发编程,就不得不提起一位老先生Doug Lea,这位老先生可不得了.看看百度百科对他的评价,一点也不为过: 假设IT的历史 ...
- 多线程高并发编程(3) -- ReentrantLock源码分析AQS
背景: AbstractQueuedSynchronizer(AQS) public abstract class AbstractQueuedSynchronizer extends Abstrac ...
- [ 高并发]Java高并发编程系列第二篇--线程同步
高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...
- java高并发编程(一)
读马士兵java高并发编程,引用他的代码,做个记录. 一.分析下面程序输出: /** * 分析一下这个程序的输出 * @author mashibing */ package yxxy.c_005; ...
- Java 多线程高并发编程 笔记(一)
本篇文章主要是总结Java多线程/高并发编程的知识点,由浅入深,仅作自己的学习笔记,部分侵删. 一 . 基础知识点 1. 进程于线程的概念 2.线程创建的两种方式 注:public void run( ...
- java高并发编程(五)线程池
摘自马士兵java并发编程 一.认识Executor.ExecutorService.Callable.Executors /** * 认识Executor */ package yxxy.c_026 ...
随机推荐
- nordic—RTC+PPI定时驱动某外设做非单次触发(本次测试为驱动GPIO口做电平翻转)
简介:在nordic的开发中使用到RTC时,对于比较通道0/1/2/3的中断来说,如果不进行相关配置(如SDK中例子,使用的RTC比较通道就只能触发一次,不能多次触发),会导致比较中断只进入一次,如果 ...
- json字符串转换对象或列表,多了字段不会报错
json字符串转换对象或列表,多了字段不会报错 //DEMO1 转换对象 应用 riskId public class Item { private String id; private String ...
- ssh_exchange_identification: Connection closed by remote host 错误解决方案
问题 今天登陆服务器时候,ssh 后返回 ssh_exchange_identification: Connection closed by remote host 错误,重试了几次,会有一定概率失败 ...
- VScode连接服务器不用每次都输入密码
VScode连接服务器不用每次都输入密码. 用git或xcode的ssh keygen生成一组不带密码的 rsa2048 的公钥id_rsa_nopasswd.pub和私钥id_rsa_nopassw ...
- Linux 内核:I2C子系统分析(1)基于子系统的驱动分析与实现
Linux 内核:I2C子系统分析(1)基于子系统的驱动分析与实现 背景 在学习高通平台的有关知识,看到一篇博客中介绍了GPIO模拟I2C设备,觉得挺有意思的. 看了看有关的实现,发现自己之前学习从L ...
- Python 潮流周刊#58:最快运行原型的语言(摘要)
本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...
- 嵌入式入门必看!调试工具安装——基于 AM64x核心板
本章节内容是为评估板串口安装USB转串口驱动程序.驱动适用于CH340.CH341等USB转串口芯片. USB转串口驱动安装 适用安装环境:Windows 7 64bit.Windows 10 64b ...
- 【冷启动#2】实用的springboot tutorial入门demo
跟着官方文档熟悉一遍创建spring工程的步骤 https://spring.io/guides/gs/spring-boot https://juejin.cn/post/7077958723829 ...
- 理解Node.js 的重要概念
Node.js是什么 Node.js是JavaScript的运行时(runtime),终于脱离了浏览器也能运行JavasScript了.同时,Node.js又暴露fs,http等对象给JS,使JS能够 ...
- Simple WPF: S3实现MINIO大文件上传并显示上传进度
最新内容优先发布于个人博客:小虎技术分享站,随后逐步搬运到博客园. 创作不易,如果觉得有用请在Github上为博主点亮一颗小星星吧! 目的 早两天写了一篇S3简单上传文件的小工具,知乎上看到了一个问题 ...