Java基于Redis实现分布式锁(原子性操作、续命)——90%以上都搞错了

tech2023-05-23  98

在使用分布式锁之前,要先思考一个问题,我们为什么要使用分布式锁?

这是因为,在分布式的部署环境下,原来的这个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 "抢票成功...."; } }

 

最新回复(0)