主要是自己在项目中(中小型项目) 有支付下单业务(只是办理VIP,没有涉及到商品库存),目前用户量还没有上来,目前没有出现问题,但是想到如果用户量变大,下单并发量变大,可能会出现一系列的问题,趁着空闲时间,做了这个demo测试相关问题。

可能遇到的问题如下:

1.订单重复

2.高并发下,性能变慢

解决方式:ThreadPoolExecutor线程池 + Queue队列

开发工具:IDEA 15

1.首先是springBoot的项目框架如下

2.业务测试流程涉及的类,如下

BusinessThread 类

package com.springboot.demo.Threads;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; /**
* Created by Administrator on 2018/5/9.
*/
@Component
@Scope("prototype")//spring 多例
public class BusinessThread implements Runnable{ private String acceptStr; 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();
}*/
}
}

TestThreadPoolManager 类

package com.springboot.demo.Threads;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component; import java.util.Map;
import java.util.Queue;
import java.util.concurrent.*; /**
* Created by Administrator on 2018/5/10.
*/
@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>(); /**
* 当线程池的容量满了,执行下面代码,将订单存入到缓冲队列
*/
final RejectedExecutionHandler handler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//订单加入到缓冲队列
msgQueue.offer(((BusinessThread) r).getAcceptStr());
System.out.println("系统任务太忙了,把此订单交给(调度线程池)逐一处理,订单号:" + ((BusinessThread) r).getAcceptStr());
}
}; /**创建线程池*/
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,则开始把缓冲队列的订单 加入到 线程池
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(); }
}

TestController 类

package com.springboot.demo;

import com.springboot.demo.Threads.TestThreadPoolManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController; import java.util.Queue;
import java.util.UUID; /**
* Created by Administrator on 2018/5/9.
*/
@RestController
public class TestController { @Autowired
TestThreadPoolManager testThreadPoolManager; /**
* 测试模拟下单请求 入口
* @param id
* @return
*/
@GetMapping("/start/{id}")
public String start(@PathVariable Long id) {
//模拟的随机数
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";
}
}

3.使用JMeter模拟并发下单请求 (JMeter使用可自行百度)

4.打印的日志说明,开始的订单直接执行插入到系统,当线程池的容量已经满了,则使用RejectedExecutionHandler方法把后面的订单添加到 Queue缓冲队列,使用ScheduledFuture方法定时(我这里是每秒一次)检查Queue队列,重新把队列里面的订单添加到线程池,执行后面的插入任务。部分日志如下

SpringBoot项目框架下ThreadPoolExecutor线程池+Queue缓冲队列实现高并发中进行下单业务的更多相关文章

  1. 源码剖析ThreadPoolExecutor线程池及阻塞队列

    本文章对ThreadPoolExecutor线程池的底层源码进行分析,线程池如何起到了线程复用.又是如何进行维护我们的线程任务的呢?我们直接进入正题: 首先我们看一下ThreadPoolExecuto ...

  2. 深入理解Java自带的线程池和缓冲队列

    前言 线程池是什么 线程池的概念是初始化线程池时在池中创建空闲的线程,一但有工作任务,可直接使用线程池中的线程进行执行工作任务,任务执行完成后又返回线程池中成为空闲线程.使用线程池可以减少线程的创建和 ...

  3. Java并发编程:4种线程池和缓冲队列BlockingQueue

    一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池.使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动 ...

  4. ThreadPoolExecutor 线程池的源码解析

    1.背景介绍 上一篇从整体上介绍了Executor接口,从上一篇我们知道了Executor框架的最顶层实现是ThreadPoolExecutor类,Executors工厂类中提供的newSchedul ...

  5. 自定义ThreadPoolExecutor带Queue缓冲队列的线程池 + JMeter模拟并发下单请求

    .原文:https://blog.csdn.net/u011677147/article/details/80271174 拓展: https://github.com/jwpttcg66/GameT ...

  6. ThreadPoolExecutor 线程池浅析

    作为Executor框架中最核心的类,ThreadPoolExecutor代表着鼎鼎大名的线程池,它给了我们足够的理由来弄清楚它. 下面我们就通过源码来一步一步弄清楚它. 内部状态 线程有五种状态:新 ...

  7. Executors、ThreadPoolExecutor线程池讲解

    官方+白话讲解Executors.ThreadPoolExecutor线程池使用 Executors:JDK给提供的线程工具类,静态方法构建线程池服务ExecutorService,也就是Thread ...

  8. 理解ThreadPoolExecutor线程池的corePoolSize、maximumPoolSize和poolSize

    我们知道,受限于硬件.内存和性能,我们不可能无限制的创建任意数量的线程,因为每一台机器允许的最大线程是一个有界值.也就是说ThreadPoolExecutor管理的线程数量是有界的.线程池就是用这些有 ...

  9. j.u.c系列(01) ---初探ThreadPoolExecutor线程池

    写在前面 之前探索tomcat7启动的过程中,使用了线程池(ThreadPoolExecutor)的技术 public void createExecutor() { internalExecutor ...

随机推荐

  1. Canal( 增量数据订阅与消费 )的理解及应用

    canal是阿里巴巴旗下的一款开源项目,纯Java开发.基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL(也支持mariaDB). 起源:早期,阿里巴巴B2B公司因为存 ...

  2. Eclipes更改字体颜色

    有图有真像 更改字体大小

  3. POJ 2001 Shortest Prefixes (Trie)

    题目链接:POJ 2001 Description A prefix of a string is a substring starting at the beginning of the given ...

  4. 【react】---react项目中如何使用iconfont

    一.路由配置 export const TabBarRouter = [ { path:"/home", icon:"\ue628", name:"首 ...

  5. Django框架(二十三)—— Django rest_framework-解析器

    解析器 一.解析器的作用 根据请求头 content-type 选择对应的解析器对请求体内容进行处理,将传过来的数据解析成字典 二.使用解析器 1.局部使用 在视图类中重定义parser_classe ...

  6. JFreeChart教程

    图表是信息的图形表示.有可用的各种工具,它可用于创建不同类型的图表. 本教程学习什么是JFreeChart?为什么需要它,并在各种方式列出一个基于Java的应用程序或独立创建不同类型的图表. JFre ...

  7. mysql中BLACKHOOL的作用

    MySQL在5.x系列提供了Blackhole引擎–"黑洞". 其作用正如其名字一样:任何写入到此引擎的数据均会被丢弃掉, 不做实际存储:Select语句的内容永远是空. 和Lin ...

  8. pyhon if分支

    在python中,最常用的就是if判断,if判断可以分为单次判断和多次判断 单次判断 if   条件 : (条件成立执行我) else:(else也可以没有,最近的else对应最近的if语句) (条件 ...

  9. filter-method 在elementUI 表格中的使用

    <el-table-column prop="pubArea" // 表格data 中对应的字段 column-key="pubArea" // 过滤条件 ...

  10. layout(布局)组件

    一.依赖于 Panel(面 板)组件和 resizable(调整大小)组件. 二.class加载方式    <div id="box" class="easyui- ...