方案一:方法上加上synchronized即可,线程安全方案,但是执行效率较低,不适合大型并发场合
方案二:采用乐观锁
controller:
@RestController
public class SecondKill {
@Autowired
SecondKillSev secondKillSev;
@RequestMapping(value = "/buy",method = RequestMethod.POST)
public Mes buy(@RequestParam("proid") String proid){
String usedId=new Random().nextInt(1000)+"";
Mes mes = secondKillSev.doSecondKill(usedId, proid);
return mes;
}
}
service:
@Service
public class SecondKillSev {
Mes message;
public SecondKillSev(){
}
@Autowired
StringRedisTemplate stringRedisTemplate;
public Mes doSecondKill(String uid, String proid){
//1.判断uid和proid非空
if(uid==null || proid==null){
return Mes.getMes("400","uid和proid不能为空");
}
//2.连接redis
ValueOperations ops = stringRedisTemplate.opsForValue();
//3.拼接key,库存key,成功用户key
String kcKey="kcKey:"+proid;
String userKey="userKey:"+proid;
//乐观锁解决超卖问题
//stringRedisTemplate默认不开启事物
stringRedisTemplate.setEnableTransactionSupport(true);
List
但是这种方案也有一个缺点,就是如果商品的数量是500,但是线程数是1000,即访问量不是非常多的情况下,会出现库存没有卖完的问题,即库存遗留问题,所以可以用lua脚本来解决;
方案三:lua脚本
local userid=KEYS[1];
local prodid=KEYS[2];
local qtkey="sk:"..prodid..":qt";
local userskey="sk:"..prodid..":user";
local userExists=redis.call("sismember",userskey,userid);
if tonumber(userExists)==1 then
return 2;
end
local num=redis.call("get",qtkey);
if tonumber(num)<=0 then
return 0;
else
redis.call("decr",qtkey);
redis.call("sadd",userskey,userid);
end
return 1
代码实现:
@Service
public class SeckillByScript {
static String secKillScript1 = "local userid=KEYS[1];n" +
"local prodid=KEYS[2];n" +
"local qtkey="sk:"..prodid..":qt";n" +
"local userskey="sk:"..prodid..":user";n" +
"local userExists=redis.call("sismember",userskey,userid);" +
"if tonumber(userExists)==1 thenn" +
" return 2;n" +
"endn" +
"local num=redis.call("get",qtkey);n" +
"if tonumber(num)<=0 thenn" +
" return 0;n" +
"elsen" +
" redis.call("decr",qtkey);n" +
" redis.call("sadd",userskey,userid);n" +
" endn" +
" return 1";
public boolean doSecKill(String userid, String prodid) {
JedisPool jedisPool = JedisPoolUtil.getJedisPool();
Jedis jedis = jedisPool.getResource();
String sha1 = jedis.scriptLoad(secKillScript1);
Object result = jedis.evalsha(sha1, 2, userid, prodid);
String reString = String.valueOf(result);
if ("0".equals(reString)) {
System.out.println("已抢空!");
return false;
} else if ("1".equals(reString)) {
System.out.println("抢购成功");
return true;
} else if ("2".equals(reString)) {
System.out.println("该用户已抢过!");
return false;
} else {
System.out.println("抢购异常");
return false;
}
}
}



