synchronized 控制并发(活动秒杀)
1.首先我们新建一个Controller用于秒杀:
package com.imooc.Controller; import com.imooc.service.impl.SeckillServiceImpl;
import lombok.extern.slf4j.Slf4j;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* Created by zhongliahi on 2018/6/11.
* 秒杀测试
*/
@RestController
@RequestMapping(value = "/skill")
@Slf4j
public class SeckillController { @Autowired
private SeckillServiceImpl seckillService; //@PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中
// URL 中的 {xxx} 占位符可以通过@PathVariable(“xxx“) 绑定到操作方法的入参中。
@GetMapping(value = "/query/{productId}")
public String query(@PathVariable String productId) throws Exception{
return seckillService.querySeckillProductInfo(productId);
} @GetMapping("/order/{productId}")
public String skill(@PathVariable String productId) throws Exception{
log.info("秒杀----productId:"+productId);
seckillService.orderProductMockDiffUser(productId); return seckillService.querySeckillProductInfo(productId);
}
}
2.建立一个Service
package com.imooc.service; /**
* Created by zhongliahi on 2018/6/11.
*/
public interface SeckillService { String queryMap(String productId); String querySeckillProductInfo(String productId); void orderProductMockDiffUser(String productId);
}
3.实现Service
package com.imooc.service.impl; import com.imooc.Exception.SellException;
import com.imooc.enums.ExceptionEnum;
import com.imooc.service.SeckillService;
import com.imooc.util.KeyUtils;
import org.springframework.stereotype.Service; import java.util.HashMap;
import java.util.Map; /**
* Created by zhonglihai on 2018/6/11.
* 秒杀Serviceimpl
* 演示
*/
@Service
public class SeckillServiceImpl implements SeckillService { /**
* 秒杀特价 1000000份
* @param productId
* @return
*/
static Map<String,Integer> products;
static Map<String,Integer> stock;
static Map<String,String> orders; static{
/**
* ,模拟多个表,商品信息表,库存表,秒杀成功订单表
*/
products =new HashMap<>();
stock=new HashMap<>();
orders=new HashMap<>();
products.put("123",1000000);
stock.put("123",1000000);
} @Override
public String queryMap(String productId) {
return "活动特价,限量:"+products.get(productId)+",还剩:"+stock.get(productId)
+"份"+",成功下单用户数:"+orders.size()+"人。";
} @Override
public String querySeckillProductInfo(String productId) {
return this.queryMap(productId);
} /**
* 主要秒杀的逻辑
* @param productId
*/
@Override
public synchronized void orderProductMockDiffUser(String productId) {
//查询该商品库存,为0则活动结束
int stockNum=stock.get(productId);
if(stockNum==0){
throw new SellException(ExceptionEnum.SECKILL_OVER);
}else{
//2.下单(模拟不同用户opendid不同)
orders.put(KeyUtils.getUniqueKey(),productId); //3.减库存
stockNum=stockNum-1;
try{
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stock.put(productId,stockNum);
}
}
}
关于压测:
压力测试是一种基本的质量保证行为,在秒杀活动中更为重要,能有效测量超卖(卖出去的比库存的多)、少卖(有买了但是没卖)等现象,目前主流的压测工具有Jmeter、LoadRunner等,老一点的有apache ab,正好本人机器装有Apace服务,因此使用apache ab做压测。
项目中,我们只添加了一件商品,productId为123,启动项目,在浏览器中查询,URL:http://127.0.0.1:8080/sell/skill/query/123,结果如下:

可以看到,项目能正常访问,查询的库存为1000000份。现在我们咋浏览器上进行秒杀,URL:http://127.0.0.1:8080/sell/skill/order/123

我们已经秒杀了一件商品,那么如何实现高并发秒杀呢?这就需要使用上面介绍的Apache ab进行压测
使用方法:安装Apache Http Services 后,配置相关环境变量,保证能在命令行直接调用。
测试命令:ab -n 1000 -c 10 http://127.0.0.1:8080/sell/skill/order/123
其中ab表示在命令行调取apache ab压测工具,-n表示发起1000条请求,-c 表示10个并发
http://127.0.0.1:8080/sell/skill/order/123 :表示测试的URL.
(注意:压测会占用大量电脑资源,特别是并发大的时候)
结果:

可以发现,秒杀还是比较快的,仅用了19秒。现在我们来查看库存;

-----------------------------------------
重点:查看库存发现虽然秒杀都成功了,但是库存量与下单成功量之和与总量不对应:999878+1000>1000000,出现了超卖现象。
下面我们在秒杀方法上加上synchronized关键字,修SeckillServiceImpl,对秒杀方法上锁

然后重启项目,在此重新查询库存:

没问题!
继续并发秒杀:

测试完成,明显可以感觉到,访问慢了很多,用了100多秒,因为我们使用了资源锁,保证每次只有一个线程去调用它。
现在我们在来查看库存。

重点:可以发现,库存与下单都是正确的,使用synchronized是一种资源控制的解决办法
那么,秒杀中直接使用synchronized进行锁控制有什么不好的地方呢?
1.无法做到细粒度的控制,在测试中,我们只有一个商品,如果有多个商品呢?
多个商品参与秒杀活动,有的人秒杀商品A、有的秒杀商品B,都要走秒杀方法,使用synchronized
会一样的慢。
2.只支持单点(单机、服务器环境),无法做到水平扩展,如果项目使用负载均衡,会出现混乱。
那么,又有什么好的办法可以解决上面提到的问题?
答案当然是有,那就是分布式锁。
synchronized 控制并发(活动秒杀)的更多相关文章
- 【总结】瞬时高并发(秒杀/活动)Redis方案(转)
转载地址:http://bradyzhu.iteye.com/blog/2270698 1,Redis 丰富的数据结构(Data Structures) 字符串(String) Redis字符串能包含 ...
- 【总结】瞬时高并发(秒杀/活动)Redis方案
1,Redis 丰富的数据结构(Data Structures) 字符串(String) Redis字符串能包含任意类型的数据 一个字符串类型的值最多能存储512M字节的内容 利用INCR命令簇(IN ...
- java并发编程学习:用 Semaphore (信号量)控制并发资源
并发编程这方面以前关注得比较少,恶补一下,推荐一个好的网站:并发编程网 - ifeve.com,上面全是各种大牛原创或编译的并发编程文章. 今天先来学习Semaphore(信号量),字面上看,根本不知 ...
- Nodejs - 如何用 eventproxy 模块控制并发
本文目标 本文的目标是获取 ZOJ 1001-1010 每道题 best solution 的作者 id,取得数据后一次性输出在控制台. 前文 如何用 Nodejs 分析一个简单页面 我们讲了如何用 ...
- Java--Semaphore控制并发线程数量
package com; import java.util.concurrent.Semaphore; /** * Created by yangyu on 16/11/28. */ /** * Se ...
- entity framework如何控制并发
entity framework如何控制并发 针对字段http://msdn.microsoft.com/en-us/library/vstudio/bb738618(v=vs.100).aspx ...
- async和enterproxy控制并发数量
聊聊并发与并行 并发我们经常提及之,不管是web server,app并发无处不在,操作系统中,指一个时间段中几个程序处于已经启动运行到完毕之间,且这几个程序都是在同一处理机上运行,并且任一个时间点只 ...
- [Go] golang无缓冲通道实现工作池控制并发
展示如何使用无缓冲的通道创建一个goroutine池,控制并发频率1.无缓冲通道保证了两个goroutine之间的数据交换2.当所有的goroutine都忙的时候,能够及时通过通道告知调用者3.无缓冲 ...
- nodejs高并发大流量的设计实现,控制并发的三种方法
nodejs高并发大流量的设计实现,控制并发的三种方法eventproxy.async.mapLimit.async.queue控制并发Node.js是建立在Google V8 JavaScript引 ...
随机推荐
- IE 8兼容:<meta http-equiv="X-UA-Compatible" content="IE=edge" /> X-UA-Compatible的解释
前言:9月份开始了,大四也真正的到来了.深知自己网页布局还有很大的缺陷,接下来打算从工作中抽时间出来模仿着一些互联网公司的网站.顺便把基础理论知识打好. 第一个目标:小米官网:后续会把练习的项目放到g ...
- [国家集训队2012]middle(陈立杰)
我是萌萌的传送门 我是另一个萌萌的传送门 脑残错误毁一下午…… 其实题解早就烂大街了,然而很久之前我只知道是二分答案+主席树却想不出来这俩玩意儿怎么一块儿用的……今天又翻了几篇题解才恍然大悟,是把权值 ...
- 记录开发Nodejs c++ addon的一些经验(四、electron)
如果我们要在electron里使用我们开发的addon,那么直接使用是不行的. 官方的解释是:Electron 同样也支持原生模块,但由于和官方的 Node 相比使用了不同的 V8 引擎,如果你想编译 ...
- 初学者需要IPython 与 Jupyter Notebook 吗?
ipython 是 jupyter notebook的前身并拥有ipython的全部功能 jupyter拥有 cell, markdown 整合的功能, 能同时运行代码, 而且是多组的 ...
- 2-2 Sass的函数功能-字符串与数字函数
Sass的函数简介 在 Sass 中除了可以定义变量,具有 @extend.%placeholder 和 mixins 等特性之外,还自备了一系列的函数功能.其主要包括: 字符串函数 数字函数 列表函 ...
- vue中全局引入bootstrap.css
1.首先在官网上下载bootstrap的压缩包(必须是官网上下载的) 将压缩包解压后引入在项目文件夹下面.如下图所示: 2.在main.js中引入 import './assets/bootstrap ...
- Session和Cookie详解(1)
面试常问的有关session和cookie的问题: 1.session在分布式环境下怎么解决 2.集群下如何保证session踩中 3.cookie的大小 4.服务器怎么识别一个用户的 5.sessi ...
- Linux->Ubuntu配置tomcat开机自动启动
Ubuntu配置tomcat开机自动启动 我们有时候会有这样一个需求: 在开机的时候就启动一个服务,比如tomcat. 我们可以这样做: 将tomcat目录下/bin中的catalina.sh拷贝到/ ...
- WBS 工作分解结构
WBS:工作分解结构(Work Breakdown Structure) 创建WBS:创建WBS是把项目 交付成果和项目工作分解成较小的,更易于管理的组成部分的过程. 主要用途WBS是一个描述思路的规 ...
- sql执行计划变更和删除缓存中执行计划的方法
将指定SQL的执行计划从共享池删除的方法 http://www.2cto.com/database/201204/126388.html Oracle SQL执行计划变更的问题 http://www. ...