synchronized的不足与redis分布式锁的使用
这里是一个简单模拟秒杀的逻辑,stock和orders为两个Map,分别模拟库存表和订单表
public void orderProductMockDiffUser(String productId)
{
//1.查询该商品库存,为0则秒杀活动结束。
int stockNum = stock.get(productId);
if(stockNum == 0) {
throw new SellException(100,"活动结束");
}else {
//2.下单(模拟不同用户id不同)
orders.put(KeyUtil.genUniqueKey(),productId);
//3.减库存(模拟在内存(或redis)中减库存)
stockNum =stockNum-1;
try {
//4.模拟一些IO或其他业务操作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stock.put(productId,stockNum);
}
}
这段逻辑存在的问题是当并发量大的时候,会造成卖出的商品数与库存减去的数目不一致

我们可以使用synchronized关键字来解决这个问题,在方法名上加上synchronized
public synchronized void orderProductMockDiffUser(String productId)
虽然synchronized可以解决数目不一致的问题,但是缺点也很明显,那就是慢,因为synchronized修饰的方法是同步的,也就是说每次只有一个线程访问这个方法,而且synchronized只适用于单点的情况。
更好的方法是使用redis分布式锁
@Component
@Slf4j
public class RedisLock { @Autowired
private StringRedisTemplate redisTemplate; /**
* 加锁
* @param key 商品id
* @param value 当前时间+超时时间
* @return
*/
public boolean lock(String key, String value) {
//setIfAbsent()也就是redis的setnx,当key不存在时设置value
if(redisTemplate.opsForValue().setIfAbsent(key, value)) {
//加锁成功
return true;
}
//当锁已存在,可以获取该锁的value,来判断是否过期
String currentValue = redisTemplate.opsForValue().get(key);
//如果锁过期
if (!StringUtils.isEmpty(currentValue)
&& Long.parseLong(currentValue) < System.currentTimeMillis()) {
//获取上一个锁的时间
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
//如果多个线程同时进入这里,则可以通过判断oldValue与currentValue是否相等来限制多个线程加锁
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
return true;
}
} return false;
} /**
* 解锁
* @param key
* @param value
*/
public void unlock(String key, String value) {
try {
String currentValue = redisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
redisTemplate.opsForValue().getOperations().delete(key);
}
}catch (Exception e) {
log.error("【redis分布式锁】解锁异常, {}", e);
}
} }
这样我们只要在秒杀逻辑开始时加上锁,逻辑结束后解锁就可以了。redis分布式锁不仅比synchronized更快,而且也适用于分布式。
public void orderProductMockDiffUser(String productId)
{
//加锁
long time=System.currentTimeMillis()+TIMEOUT;
if(!redisLock.lock(productId,String.valueOf(time))){
throw new SellException(ResultEnum.REDIS_LOCK_FAIL);
}
//1.查询该商品库存,为0则活动结束。
int stockNum = stock.get(productId);
if(stockNum == 0) {
throw new SellException(100,"活动结束");
}else {
//2.下单(模拟不同用户openid不同)
orders.put(KeyUtil.genUniqueKey(),productId);
//3.减库存(模拟在内存(或redis)中减库存)
stockNum =stockNum-1;
try {
//4.模拟一些IO或其他业务操作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stock.put(productId,stockNum);
} //解锁
redisLock.unlock(productId,String.valueOf(time));
}
synchronized的不足与redis分布式锁的使用的更多相关文章
- 用压测模拟并发、并发处理(synchronized,redis分布式锁)
使用工具:Apache an 测压命令: ab -n 100 -c 100 http://www.baidu.com -n代表模拟100个请求,-c代表模拟100个并发,相当于100个人同时访问 ab ...
- redis分布式锁实践
分布式锁在多实例部署,分布式系统中经常会使用到,这是因为基于jvm的锁无法满足多实例中锁的需求,本篇将讲下redis如何通过Lua脚本实现分布式锁,不同于网上的redission,完全是手动实现的 我 ...
- Redis分布式锁的try-with-resources实现
Redis分布式锁的try-with-resources实现 一.简介 在当今这个时代,单体应用(standalone)已经很少了,java提供的synchronized已经不能满足需求,大家自然 而 ...
- 关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁
首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在j ...
- Lua脚本在redis分布式锁场景的运用
目录 锁和分布式锁 锁是什么? 为什么需要锁? Java中的锁 分布式锁 redis 如何实现加锁 锁超时 retry redis 如何释放锁 不该释放的锁 通过Lua脚本实现锁释放 用redis做分 ...
- Redis 分布式锁及缓存注释的使用方法
使用工具:Apache an 测压命令: ab -n 100 -c 100 http://www.baidu.com -n代表模拟100个请求,-c代表模拟100个并发,相当于100个人同时访问 ab ...
- SpringBoot集成Redis分布式锁以及Redis缓存
https://blog.csdn.net/qq_26525215/article/details/79182687 集成Redis 首先在pom.xml中加入需要的redis依赖和缓存依赖 < ...
- Redis分布式锁的实现
前段时间,我在的项目组准备做一个类似美团外卖的拼手气红包[第X个领取的人红包最大],基本功能实现后,就要考虑这一操作在短时间内多个用户争抢同一资源的并发问题了,类似于很多应用如淘宝.京东的秒杀活动场景 ...
- springboot项目:Redis分布式锁的使用(模拟秒杀系统)
模拟秒杀系统: 第一步:编写Service package com.payease.service; /** * liuxiaoming * 2017-12-14 */ public interfac ...
随机推荐
- avalon在公共页面里面写的功能,怎么让某些页面不引用到这个方法和html?
每一个页面都用用到head.html里面的东西,但是有些页面我不想调用这个js,但是在head.html我已经调用了怎么办? 比如我是这个页面不需要去调用 https://*******/mytry ...
- svn服务备份与还原
1.dump备份方式: svnadmin dump /data/svn/xxxx > /data/beifen/`date +/%Y%m%d`.bak xxxx:项目名称(项目库) 将xxxx这 ...
- OpenFOAM-双柱及群柱绕流
这次的教程是紧接前几次的教程,设置与前几次教程类似,但是对于设置上稍微有一点点区别,就是在设置值的时候,出现了$internalField,其实这是一个字符串替换,就是在出现$internalFiel ...
- pymongo helper
import pymongo import click # 数据库基本信息 db_configs = { 'type': 'mongo', 'host': '127.0.0.1', 'port': ' ...
- Python2和Python3共存问题
前提条件:先准备一个新电脑 1.下载Python2和Python3的安装包,直接官网下载:https://www.python.org/download 2.配置环境变量,可以手动配置,也可以安装的时 ...
- ls命令的简单实现
ls命令的简单实现 目标:简单的实现ls命令 实现的mic_ls命令主要功能 1.循环遍历目录 2.列出目标目录所有的子目录和文件 3.列出文件的文件权限,所有者,文件大小等详细信息 参数 -r 循环 ...
- DNGuard HVM Unpacker(3.71 trial support and x64 fixed)
DNGuard HVM Unpacker(3.71 trial support and x64 fixed) Gr8 news. Finally got the x64 crash fixed. DN ...
- MFC GDI+显示GIF文件《转》
在头文件里面添加: Image* image; GUID Guid ; UINT frameCount; UINT framePos;ULONG_PTR gdiplusToken; afx_msg v ...
- pip install失败报错解决方案
cmd pip install 某些包时报错 pip install Consider using the `--user` option or check the permissions. 只需要p ...
- Docker Error response from daemon: driver failed programming external connectivity on endpoint webserver
按照官网的配置, https://docs.docker.com/docker-for-windows/ 当执行到第 5 步时 如果当前你的电脑 80 端口已经被别的程序占用,运行以上命令就会报错,报 ...