主题:对于传统的synchronized同步方案来说,redis分布式锁更加高效。
以商品秒杀为例,当我们要执行以下方法秒杀时:
public void orderProductMockDiffUser(String productId);synchronized只能允许单线程执行该方法,但是对于不同的商品来说,没不要单线程执行,只要针对不同的productId单线程执行该方法即可。因此需要分布式锁。
完整代码
包含查询代码和秒杀代码
package com.imooc.service.impl;
import com.imooc.exception.SellException;
import com.imooc.service.RedisLock;
import com.imooc.service.SecKillService;
import com.imooc.utils.KeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/**
* @program: springboot_wx_sell
* @description: 秒杀
* @author: Gu
* @create: 2019-04-04 19:32
**/
@Service
public class SecKillServiceIml implements SecKillService {
//加锁超时时间
private static final int TIME_OUT = 10 * 1000;
/**
* 模拟商品表,库存表,订单表
*/
static Map<String, Integer> products;
static Map<String, Integer> stock;
static Map<String, String> orders;
@Autowired
private RedisLock redisLock;
static {
products = new HashMap<>();
stock = new HashMap<>();
orders = new HashMap<>();
products.put("123456", 10000);
stock.put("123456", 10000);
}
private String queryMap(String productId) {
return "国庆活动,皮蛋粥特价,限量份"
+ products.get(productId)
+" 还剩:" + stock.get(productId)+" 份"
+" 该商品成功下单用户数目:"
+ orders.size() +" 人" ;
}
@Override
public String querySecKillProductInfo(String productId) {
return this.queryMap(productId);
}
@Override
public void orderProductMockDiffUser(String productId) {
//加锁
long time = System.currentTimeMillis() + TIME_OUT;
redisLock.lock(productId, String.valueOf(time));
//1.该商品库存查询,为0则活动结束
int stockNum = stock.get(productId);
if (0 == stockNum) {
throw new SellException(100, "活动结束");
}else {
//2.模拟下单,减库存(不同用户id,KeyUtil.genUniquekey())
orders.put(KeyUtil.genUniquekey(), productId);
//3.减库存
stockNum = stockNum -1;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stock.put(productId, stockNum);
}
//解锁
redisLock.unLock(productId, String.valueOf(time));
}
}
redis加锁和解锁
这里要注意加锁超时情况!
package com.imooc.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* @program: springboot_wx_sell
* @description: redis锁
* @author: Gu
* @create: 2019-04-08 19:29
**/
@Component
@Slf4j
public class RedisLock {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 加锁
*
* @param key
* @param value
* @return
*/
public boolean lock(String key, String value) {
if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}
//currentValue=A 这两个线程的value都是B 其中一个线程拿到锁
String currentValue = redisTemplate.opsForValue().get(key);
//如果锁过期
if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {
//获取上一个锁的时间
String oldVaue = redisTemplate.opsForValue().getAndSet(key, value);
if (!StringUtils.isEmpty(oldVaue) && oldVaue.equals(currentValue)) {
return true;
}
}
return false;
}
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);
}
}
}
ab压力测试
ab -n 60 -c 3 http://127.0.0.1:8080/sell/skill/order/123456
了解缓存的使用。
网友评论