自定义ThreadPoolExecutor带Queue缓冲队列的线程池 + JMeter模拟并发下单请求
.原文:https://blog.csdn.net/u011677147/article/details/80271174
拓展:
https://github.com/jwpttcg66/GameThreadPool/blob/85bb392151324e68addec355d85d9ce22b4ab1e2/src/test/java/com/snowcattle/game/thread/ThreadPoolTest.java
游戏中常用的线程池,顺序队列和非顺序队列
@RestController
public class TestController { @Autowired
TestThreadPoolManager testThreadPoolManager; /**
* 测试模拟下单请求 入口
* @param id
* @return
*/
@GetMapping("/start/{idhaha}")
public String start(@PathVariable Long idhaha) {
//模拟的随机数
String orderNo = System.currentTimeMillis() + UUID.randomUUID().toString(); testThreadPoolManager.addOrders(orderNo); return "Test ThreadPoolExecutor start";
} /**
* 停止服务
* @param id
* @return
*/
@GetMapping("/end/{id}")
public String end(@PathVariable Long id) { testThreadPoolManager.shutdown(); Queue q = testThreadPoolManager.getMsgQueue();
System.out.println("关闭了线程服务,还有未处理的信息条数:" + q.size());
return "Test ThreadPoolExecutor start";
}
}
@Component
public class TestThreadPoolManager implements BeanFactoryAware { //用于从IOC里取对象
private BeanFactory factory; //如果实现Runnable的类是通过spring的application.xml文件进行注入,可通过 factory.getBean()获取,这里只是提一下 private final static int CORE_POOL_SIZE = 2; private final static int MAX_POOL_SIZE = 10; private final static int KEEP_ALIVE_TIME = 0;
// 线程池所使用的缓冲队列大小
private final static int WORK_QUEUE_SIZE = 50; @Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
factory = beanFactory;
} /**
* 线程队列:用于储存在队列中的订单,防止重复提交,在真实场景中,可用redis代替 验证重复
*/
Map<String, Object> cacheMap = new ConcurrentHashMap<>(); /**
* 线程缓冲队列:订单的缓冲队列,当线程池满了,则将订单存入到此缓冲队列
*/
Queue<Object> msgQueue = new LinkedBlockingQueue<Object>(); /**
* 在使用线程池并且使用有界队列的时候,如果队列满了,任务添加到线程池的时候就会有问题,针对这些问题java线程池提供了以下几种策略:
* AbortPolicy
* DiscardPolicy
* DiscardOldestPolicy
* CallerRunsPolicy
* 自定义
*
* 当前采用的就是自定义:线程池的容量满了,执行下面代码,将订单存入到缓冲队列
*/
final RejectedExecutionHandler handler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//订单加入到缓冲队列
msgQueue.offer(((BusinessThread) r).getAcceptStr());
System.out.println("系统任务太忙了(有界队列已经满了),把此订单交给(调度线程池)逐一处理,订单号:" + ((BusinessThread) r).getAcceptStr());
}
}; /**创建线程池
*
* 概念解释及原理(无实例):
* https://uule.iteye.com/blog/1123185
* https://www.cnblogs.com/trust-freedom/p/6594270.html
* https://www.cnblogs.com/zedosu/p/6665306.html
*
*含实例的:
* https://blog.csdn.net/x631617479/article/details/83001198 :自定义连接池ThreadPoolExecutor执行顺序
* https://blog.csdn.net/changyuan101/article/details/50755157 :ThreadPoolExecutor自定义RejectedExecutionHandler当队列满时改为调用BlockingQueue. put来实现生产者的阻塞
*
* 处理任务的优先级为:
* corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用(调度线程池)handler处理被拒绝的任务。
*
*
* */
final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
CORE_POOL_SIZE, // 线程池维护线程的最少数量
MAX_POOL_SIZE, // 线程池维护线程的最大数量
KEEP_ALIVE_TIME, // 线程池维护线程所允许的空闲时间
TimeUnit.SECONDS, // 线程池维护线程所允许的空闲时间的单位
new ArrayBlockingQueue(WORK_QUEUE_SIZE), // 线程池所使用的缓冲队列
this.handler // 线程池对拒绝任务的处理策略
); /**将任务加入订单线程池*/
public void addOrders(String orderId){
System.out.println("此订单准备添加到线程池,订单号:" + orderId);
//验证当前进入的订单是否已经存在
if (cacheMap.get(orderId) == null) {
cacheMap.put(orderId, new Object());
BusinessThread businessThread = new BusinessThread(orderId);
threadPool.execute(businessThread);
}
} /**
* 线程池的定时任务----> 称为(调度线程池)。此线程池支持 定时以及周期性执行任务的需求。
*/
final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5); /**
* 检查(调度线程池),每秒执行一次,查看订单的缓冲队列是否有 订单记录,则重新加入到线程池
*/
final ScheduledFuture scheduledFuture = scheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
//判断缓冲队列是否存在记录
if(!msgQueue.isEmpty()){
//当线程池的队列容量少于 WORK_QUEUE_SIZE(缓冲队列max),则开始把缓冲队列的订单 加入到 线程池
if (threadPool.getQueue().size() < WORK_QUEUE_SIZE) {
String orderId = (String) msgQueue.poll();
BusinessThread businessThread = new BusinessThread(orderId);
threadPool.execute(businessThread);
System.out.println("(调度线程池)缓冲队列出现订单业务,重新添加到线程池,订单号:"+orderId);
}
}
}
}, 0, 1, TimeUnit.SECONDS); /**获取消息缓冲队列*/
public Queue<Object> getMsgQueue() {
return msgQueue;
} /**终止订单线程池+调度线程池*/
public void shutdown() {
//true表示如果定时任务在执行,立即中止,false则等待任务结束后再停止
System.out.println("终止订单线程池+调度线程池:"+scheduledFuture.cancel(false));
scheduler.shutdown();
threadPool.shutdown(); }
}
@Component
@Scope("prototype")//spring 多例
public class BusinessThread implements Runnable{ private String acceptStr; public BusinessThread() {
super();
} public BusinessThread(String acceptStr) {
this.acceptStr = acceptStr;
} public String getAcceptStr() {
return acceptStr;
} public void setAcceptStr(String acceptStr) {
this.acceptStr = acceptStr;
} @Override
public void run() {
//业务操作
System.out.println("多线程已经处理订单插入系统,订单号:"+acceptStr); //线程阻塞
/*try {
Thread.sleep(1000);
System.out.println("多线程已经处理订单插入系统,订单号:"+acceptStr);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
自定义ThreadPoolExecutor带Queue缓冲队列的线程池 + JMeter模拟并发下单请求的更多相关文章
- SpringBoot项目框架下ThreadPoolExecutor线程池+Queue缓冲队列实现高并发中进行下单业务
主要是自己在项目中(中小型项目) 有支付下单业务(只是办理VIP,没有涉及到商品库存),目前用户量还没有上来,目前没有出现问题,但是想到如果用户量变大,下单并发量变大,可能会出现一系列的问题,趁着空闲 ...
- springboot2.0+线程池+Jmeter以模拟高并发
声明:原创在这里https://blog.csdn.net/u011677147/article/details/80271174,在此也谢谢哥们. 1.目录结构 2.BusinessThread.j ...
- 【Java并发】并发队列与线程池
并发队列 阻塞队列与非阻塞队 ConcurrentLinkedQueue BlockingQueue ArrayBlockingQueue LinkedBlockingQueue PriorityBl ...
- Java线程池ThreadPoolExecutor使用和分析(三) - 终止线程池原理
相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...
- Tomcat如何使用线程池处理远程并发请求
Tomcat如何使用线程池处理远程并发请求 通过了解学习tomcat如何处理并发请求,了解到线程池,锁,队列,unsafe类,下面的主要代码来自 java-jre: sun.misc.Unsafe j ...
- Linux中epoll+线程池实现高并发
服务器并发模型通常可分为单线程和多线程模型,这里的线程通常是指“I/O线程”,即负责I/O操作,协调分配任务的“管理线程”,而实际的请求和任务通常交由所谓“工作者线程”处理.通常多线程模型下,每个线程 ...
- 超越线程池:Java并发并没有你想的那么糟糕
转载: 超越线程池:Java并发并没有你想的那么糟糕
- python 线程队列、线程池、全局解释器锁GIL
一.线程队列 队列特性:取一个值少一个,只能取一次,没有值的时候会阻塞,队列满了,也会阻塞 queue队列 :使用import queue,用法与进程Queue一样 queue is especial ...
- 第三十四天- 线程队列、线程池(map/submit/shutdown/回调函数)
1.线程列队 queue队列 :使用import queue,用法与进程Queue一样 class queue.Queue(maxsize=0) # 先进先出: q = queue.Queue(3) ...
随机推荐
- excle
1.固定某行列 如果要使一行不动,将光标定位于A2单击中,单击菜单"窗口----冻结窗格" 一行一列的,光标定位于B2单元格中,其它的以此类推 2.自动排序号 自动排序号,就是在某 ...
- UVa Live 3635 - Pie 贪心,较小的可能不用 难度: 2
题目 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_pr ...
- day041 前端HTML CSS基本选择器(未整理完毕)
标签: <b> :加粗 <i> :倾斜体 <u>: 下划线 <s>: 删除线 <p>:段落 <h1> - <h6> ...
- 删除Mac OS X中Finder文件打开方式列表的重复程序或失效的
清理列表, 可以在终端中输入下面提供的一行命令: /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices ...
- 《Python》常用内置模块
一.time模块(时间模块) 三种格式: 1.时间戳时间(timestamp):浮点数,秒为单位,从1970年1月1日0时距今的时间 1970.1.1 0:0:0 英国伦敦时间(开始时间) 1970 ...
- shell脚本总结
1.变量 A: 定义变量A=1,只会对自己所在的shell进程有效 B: 定义变量export B=1,会对自己所在的shell进程及其子进程生效 C: 在script.sh脚本中定义的变量,在当前 ...
- 1.2socket服务器使用多线程
socket服务器代码如下 # -*- coding: utf-8 -*-from socket import * import time import threading,_thread as th ...
- 究竟 javascript 错误处理有哪些类型?
有时候,在自己封装的工具函数中,不传参或传入了错误类型的参数,也要适当的抛出一些错误以示警告:使用框架不正常情况下也会抛出错误,如果对错误一无所知,便无从下手调试.综合上述,了解错误的处理机制是多么必 ...
- C#窗体换肤
Form1.cs using System;using System.Collections.Generic;using System.ComponentModel;using System.Data ...
- select语句的高级应用及实例
本文介绍的select高级应用主要包括:联合查询.分组查询.嵌套查询和限定查询数目等,与实例对照演示,所使用数据库为sqlite3. 部门表(dept) CREATE TABLE dept( id I ...