转:https://segmentfault.com/a/1190000011421467

废话不多说,首先分享一个业务场景-抢购。一个典型的高并发问题,所需的最关键字段就是库存,在高并发的情况下每次都去数据库查询显然是不合适的,因此把库存信息存入Redis中,利用redis的锁机制来控制并发访问,是一个不错的解决方案。

首先是一段业务代码:

@Transactional
public void orderProductMockDiffUser(String productId){//1.查库存int stockNum  = stock.get(productId);if(stocknum == 0){throw new SellException(ProductStatusEnum.STOCK_EMPTY);//这里抛出的异常要是运行时异常,否则无法进行数据回滚,这也是spring中比较基础的   }else{//2.下单orders.put(KeyUtil.genUniqueKey(),productId);//生成随机用户id模拟高并发sotckNum = stockNum-1;try{Thread.sleep(100);} catch (InterruptedExcption e){e.printStackTrace();}stock.put(productId,stockNum);}
}

这里有一种比较简单的解决方案,就是synchronized关键字。

public synchronized void orderProductMockDiffUser(String productId)

这就是java自带的一种锁机制,简单的对函数加锁和释放锁。但问题是这个实在是太慢了,感兴趣的可以可以写个接口用apache ab压测一下。

ab -n 500 -c 100 http://localhost:8080/xxxxxxx

下面就是redis分布式锁的解决方法。首先要了解两个redis指令
SETNX 和 GETSET,可以在redis中文网上找到详细的介绍。
SETNX就是set if not exist的缩写,如果不存在就返回保存value并返回1,如果存在就返回0。
GETSET其实就是两个指令GET和SET,首先会GET到当前key的值并返回,然后在设置当前Key为要设置Value。

首先我们先新建一个RedisLock类:

@Slf4j
@Component
public class RedisService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;/**** 加锁* @param key* @param value 当前时间+超时时间* @return 锁住返回true*/public boolean lock(String key,String value){if(stringRedisTemplate.opsForValue().setIfAbsent(key,value)){//setNX 返回booleanreturn true;}//如果锁超时 ***String currentValue = stringRedisTemplate.opsForValue().get(key);if(!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue)<System.currentTimeMillis()){//获取上一个锁的时间String oldvalue  = stringRedisTemplate.opsForValue().getAndSet(key,value);if(!StringUtils.isEmpty(oldvalue)&&oldvalue.equals(currentValue)){return true;}}return false;}/**** 解锁* @param key* @param value* @return*/public void unlock(String key,String value){try {String currentValue = stringRedisTemplate.opsForValue().get(key);if(!StringUtils.isEmpty(currentValue)&&currentValue.equals(value)){stringRedisTemplate.opsForValue().getOperations().delete(key);}} catch (Exception e) {log.error("解锁异常");}}
}

这个项目是springboot的项目。首先要加入redis的pom依赖,该类只有两个功能,加锁和解锁,解锁比较简单,就是删除当前key的键值对。我们主要来说一说加锁这个功能。
首先,锁的value值是当前时间加上过期时间的时间戳,Long类型。首先看到用setiFAbsent方法也就是对应的SETNX,在没有线程获得锁的情况下可以直接拿到锁,并返回true也就是加锁,最后没有获得锁的线程会返回false。 最重要的是中间对于锁超时的处理,如果没有这段代码,当秒杀方法发生异常的时候,后续的线程都无法得到锁,也就陷入了一个死锁的情况。我们可以假设CurrentValue为A,并且在执行过程中抛出了异常,这时进入了两个value为B的线程来争夺这个锁,也就是走到了注释*的地方。currentValue==A,这时某一个线程执行到了getAndSet(key,value)函数(某一时刻一定只有一个线程执行这个方法,其他要等待)。这时oldvalue也就是之前的value等于A,在方法执行过后,oldvalue会被设置为当前的value也就是B。这时继续执行,由于oldValue==currentValue所以该线程获取到锁。而另一个线程获取的oldvalue是B,而currentValue是A,所以他就获取不到锁啦。多线程还是有些乱的,需要好好想一想。
接下来就是在业务代码中加锁啦:首要要@Autowired注入刚刚RedisLock类,不要忘记对这个类加一个@Component注解否则无法注入

private static final int TIMEOUT= 10*1000;
@Transactional
public void orderProductMockDiffUser(String productId){long time = System.currentTimeMillions()+TIMEOUT;if(!redislock.lock(productId,String.valueOf(time)){throw new SellException(101,"换个姿势再试试")}//1.查库存int stockNum  = stock.get(productId);if(stocknum == 0){throw new SellException(ProductStatusEnum.STOCK_EMPTY);//这里抛出的异常要是运行时异常,否则无法进行数据回滚,这也是spring中比较基础的   }else{//2.下单orders.put(KeyUtil.genUniqueKey(),productId);//生成随机用户id模拟高并发sotckNum = stockNum-1;try{Thread.sleep(100);} catch (InterruptedExcption e){e.printStackTrace();}stock.put(productId,stockNum);}redisLock.unlock(productId,String.valueOf(time));
}

大功告成了!比synchronized快了不知道多少倍,再也不会被老板骂了!

转载于:https://www.cnblogs.com/duende99/p/11553745.html

Redis分布式锁解决抢购问题相关推荐

  1. 简单介绍redis分布式锁解决表单重复提交的问题

    在系统中,有些接口如果重复提交,可能会造成脏数据或者其他的严重的问题,所以我们一般会对与数据库有交互的接口进行重复处理.本文就详细的介绍一下redis分布式锁解决表单重复提交,感兴趣的可以了解一下 假 ...

  2. 07: redis分布式锁解决超卖问题

    07: redis分布式锁解决超卖问题 参考文章: (1)07: redis分布式锁解决超卖问题 (2)https://www.cnblogs.com/xiaonq/p/12328934.html 备 ...

  3. SpringBoot 使用 Redis 分布式锁解决并发问题

    问题背景 现在的应用程序架构中,很多服务都是多副本运行,从而保证服务的稳定性.一个服务实例挂了,其他服务依旧可以接收请求.但是服务的多副本运行随之也会引来一些分布式问题,比如某个接口的处理逻辑是这样的 ...

  4. spring项目使用redis分布式锁解决重复提交问题

    场景演示 假设有一个录入学生信息的功能,为了便于演示,要求不能有重名的学生,并且数据库对应字段没有做唯一限制. @GetMapping("/student/{name}")publ ...

  5. 探讨Redis分布式锁解决优惠券拼抢问题

    一.什么是分布式锁 分布式锁是控制不同系统之间访问共享资源的一种锁实现,如果不同的系统或同一个系统的不同主机之间共享了某个资源时,往往需要互斥来防止彼此干扰来确保数据一致性. 二.为什么需要分布式锁 ...

  6. redis mysql 解决超卖_Redis 分布式锁解决超卖问题

    Redis 分布式锁解决超卖问题 1,Redis 事物介绍 1. Redis 事物是可以一次执行多个命令, 本质是一组命令的集合. 2. 一个事务中的所有命令都会序列化, 按顺序串行化的执行而不会被其 ...

  7. Redis分布式锁实现

    redis分布式锁解决多个应用进程间同步操作 import java.util.List; import java.util.UUID;import redis.clients.jedis.Jedis ...

  8. 【使用Redis分布式锁实现优惠券秒杀功能】-Redis学习笔记05

    前言 本章节主要实现限时.限量优惠券秒杀功能,并利用分布式锁解决<超卖问题>.<一人一单问题>. 一.优惠券下单基本功能实现 1.功能介绍及流程图 2.代码实现 @Resour ...

  9. Redis分布式锁防止缓存击穿

    缓存击穿 和缓存穿透不同的是,缓存击穿是指:缓存中没有,但是数据库中存在的热点数据. 例如:首页的热点新闻,并发访问量非常大的热点数据,如果缓存过期失效,服务器会去查询DB,这时候如果大量的并发去查询 ...

最新文章

  1. 考研最惨专业排行榜,你上榜了吗?
  2. hibernate+mysql时,会出现事物不管用
  3. 二分图带权最大匹配费用流_一文掌握阻抗匹配
  4. 超过200m文件发送_微信支持发送大文件了,高清视频不再压缩
  5. SQL Server 创建游标(cursor)
  6. 板材品牌之生态板吊顶好还是桑拿板好
  7. sqlserver数据库得管理及维护
  8. 【正点原子I.MX6U-MINI应用篇】5、嵌入式Linux在LCD上显示BMP、JPG、PNG图片
  9. 在matlab中画收敛域,已知用下列差分方程描述的一个线性移不变因果系统(用MATLAB方法求解)。y(n)=y(n-1)+y(n-2)十x(n-1.. - 上学吧找答案...
  10. oracle numa map size mismatch,Oracle启动时提示map size mismatch; abort
  11. SiameseNet
  12. 一维元胞自动机生命游戏
  13. c语言年历显示主要思路,c语言程序设计年历显示分析.doc
  14. 十进制整数,转换成八进制和十六进制数并输出。
  15. 电化学甲醛气体传感器
  16. 女程序媛的神奇修仙路
  17. 环卫事业编2000工资,为何本科生抢着干?这3点好处,他们不会说
  18. iOS - 适配iOS 11
  19. http://39.98.219.132 题库标准答案(题库序号:179)之判断素数
  20. 教你如何用python绘图

热门文章

  1. poj2976 Dropping tests
  2. Android开发常用的插件及工具
  3. Sublime 插件- px 转rem
  4. 【代码笔记】iOS-翻书效果的实现
  5. php安装编译时 configure: error: Cannot find OpenSSL's evp.h
  6. PHP 如何判断当前用户已在别处登录
  7. 神奇的pdfkit工具——将字符串保存为pdf文件
  8. Rust 编程 前言
  9. 性能测试的重要意义(一)
  10. Hadoop MapReduce概念学习系列之MPI和MapReduce(十三)