synchronized的不足与redis分布式锁的使用
2019-09-17 10:23:05来源:博客园 阅读 ()
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)); }
原文链接:https://www.cnblogs.com/skychmz/p/11498782.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:JDK环境变量的配置
- redis缓存 2020-06-12
- Spring Boot 2.3.0 新特性Redis 拓扑动态感应 2020-06-11
- 作为一个面试官,我想问问你Redis分布式锁怎么搞? 2020-06-10
- 分布式锁没那么难,手把手教你实现 Redis 分布锁!|保姆级教 2020-06-08
- Redis企业级数据备份与恢复方案 2020-06-04
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash