在使用分布式锁之前,要先思考一个问题,我们为什么要使用分布式锁?
这是因为,在分布式的部署环境下,原来的这个synchronized 只能在当前的JVM中加锁,不能跨JVM实现加锁,所以这种情况下我们就急需要使用分布式的锁来完成锁的功能。
分布式锁有很多种实现方式,基于zookeeper、基于数据库排他锁、基于缓存redis/memcache...
我们使用基于缓存的redis实现分布式锁。
public class TrainTickService {
private Logger logger= LoggerFactory.getLogger(TrainTickService.class);
@Autowired
private StringRedisTemplate stringRedisTemplate;
//这个是锁对象
@Autowired
private DistributeRedisLock distributeRedisLock;
/**
* 这个方法就是专门用来减售火车票的这样一个系统
* 秒杀抢火车票
*
* 30秒没有运行完 怎么办?
*/
public String produceStock(){
String lock="lock";
String value=UUID.randomUUID().toString();
MyThread myThread=null;
//设置个字符串
try {
// setnx命令
//Boolean tag = stringRedisTemplate.opsForValue().setIfAbsent(lock, "");
//给这个key设置过期时间
//执行下面这一句话的时候突然死了? 是不是又出现死锁了
//stringRedisTemplate.expire(lock,30,TimeUnit.SECONDS);
//这个命令的底层实际上 也运行的是 咋们的命令 在底层他是怎样来实现原子性的呢?
// 充分的利用了 redis和lua脚本 在C的层面上来实现原子性的a
//setnx 只有这个可以不存在的时候 这个才会被删除
Boolean tag=stringRedisTemplate.opsForValue().setIfAbsent(lock,value,30,TimeUnit.SECONDS);
if (!tag) {
//递归调用(让来的人中就要抢我们数据库中拥有的这个值)
produceStock();
return "目前排队人数过多...请稍后重试";
}
//超过了15
//开一个守护线程
myThread = new MyThread(lock);
myThread.setDaemon(true);
myThread.start();
// 每隔设置时间的3分支1就进行线程的续命
//一会初始值的时候我将火车票 放到redis中去
//减去库存
//去Redis中将库存数据给取出来
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("traintickes"));
//首先要判断下 这个库存是否>0
if (stock > 0) { //说明可以减去库存
int rStock = stock - 1;
//下一步:将真实的库存放到咋们的Redis中去
stringRedisTemplate.opsForValue().set("traintickes", String.valueOf(rStock));
logger.info("扣减库存成功....剩余库存:" + rStock);
} else { //说明不能扣减库存
logger.info("库存扣减失败、库存是负数、不足...");
} //已经用了15秒钟
}finally {
if(value.equals(stringRedisTemplate.opsForValue().get(lock))){
//在这里要中断这个线程
myThread.stop();
stringRedisTemplate.delete(lock);
logger.info("释放锁成功....");
}
}
return "抢票成功....";
}
/**
* 使用后台线程进行续命
* 守护线程
* 在主线程下 如果有一个守护线程 这个守护线程的生命周期 跟主线程是同生死的
*/
class MyThread extends Thread{
String lock;
public MyThread(String lock){
this.lock=lock;
}
@Override
public void run() {
while (true){
//干这个事情、
try{
Thread.sleep(10000);
}catch (Exception err){
}
//假设程序还活着 那么说明需要续命
logger.info("续费中....");
stringRedisTemplate.expire(lock,30,TimeUnit.SECONDS);
}
}
}
/**
* 使用redisson来完成
* @return
*/
public String produceStockRedisson(){
String lock="lock";
try {
boolean lock1 = distributeRedisLock.lock(lock);
if(true==lock1){//说明加锁成功
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("traintickes"));
//首先要判断下 这个库存是否>0
if (stock > 0) { //说明可以减去库存
int rStock = stock - 1;
//下一步:将真实的库存放到咋们的Redis中去
stringRedisTemplate.opsForValue().set("traintickes", String.valueOf(rStock));
logger.info("扣减库存成功....剩余库存:" + rStock);
} else { //说明不能扣减库存
logger.info("库存扣减失败、库存是负数、不足...");
} //已经用了15秒钟
}else{
return "当前的排队人数过多...";
}
}finally {
distributeRedisLock.unlock(lock);
}
return "抢票成功....";
}
}