springboot之redis的分布式锁

一、 测试

    @Autowired
    private RedisLockHelper redisLockHelper;

    private static final String LOCK ="lock:equipment";
   
    // 14: 48执行
    //@Scheduled(cron = "0 48 14 ? * *")
    public void orderSync()  {
        long time = System.currentTimeMillis() + (10*1000);
        //进行加锁操作
        if (redisLockHelper.lock(LOCK, time)) {
            System.out.println("加锁");
        }else {
            System.out.println("解锁");
            redisLockHelper.unlock(LOCK,time);
        }
    }

 

二、操作redis的工具类

package com.kmnfsw.util;


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

/**
 *
 * @author wyg
 */
@Component
@Slf4j
public class RedisLockHelper {
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 加锁
     * @param target   唯一标志
     * @param timeStamp  当前时间+超时时间 也就是时间戳
     * @return
     */
    public boolean lock(String target, long timeStamp){
        // 如果键不存在则新增,存在则不改变已经有的值。
        if(redisTemplate.opsForValue().setIfAbsent(target,timeStamp)){
            return true;
        }

        // 判断锁超时 - 防止原来的操作异常,没有运行解锁操作  防止死锁
        long currentLock = (long) redisTemplate.opsForValue().get(target);
        // 如果锁过期 currentLock不为空且小于当前时间
        if(currentLock < System.currentTimeMillis()){
            // 获取上一个锁的时间value 对应getset,如果lock存在 设置给过来的值,并返回旧的值
            long preLock = (long) redisTemplate.opsForValue().getAndSet(target,timeStamp);

            // 假设两个线程同时进来这里,因为key被占用了,而且锁过期了。获取的值currentLock=A(get取的旧的值肯定是一样的),两个线程的timeStamp都是B,key都是K.锁时间已经过期了。
            // 而这里面的getAndSet一次只会一个执行,也就是一个执行之后,上一个的timeStamp已经变成了B。只有一个线程获取的上一个值会是A,另一个线程拿到的值是B。
            if(preLock==currentLock){
                // preLock不为空且preLock等于currentLock,也就是校验是不是上个对应的商品时间戳,也是防止并发
                return true;

            }
        }
        return false;
    }


    /**
     * 解锁
     * @param target
     * @param timeStamp
     */
    public void unlock(String target,long timeStamp){
        try {
            long currentValue = (long) redisTemplate.opsForValue().get(target);
            if( currentValue<=timeStamp){
                // 删除锁状态
                redisTemplate.opsForValue().getOperations().delete(target);
            }
        } catch (Exception e) {
            log.error("警报!警报!警报!解锁异常{}",e);
        }
    }
}

还有一种写法:


   public boolean lock(String target) {

 String uuid = UUID.randomUUID().toString();
        // 如果键不存在则新增,存在则不改变已经有的值。设置超时时间(解决异常,解决绝宕机)
        if (redisTemplate.opsForValue().setIfAbsent(target, uuid, 10, TimeUnit.SECONDS)) {
            return true;
        }

        try {
            long Lock = (long) redisTemplate.opsForValue().get(target);
            //业务代码
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //解决自己的锁自己释放
            if(uuid.equals(redisTemplate.opsForValue().get(target))){
                redisTemplate.delete(target);
            }
        }
        //注:这里有个缺点,当执行代码的时间超过锁的过期时间,会导致锁被释放掉,或者导致永久失效,这是需要加线程延长锁的时间或者用redission框架
}
       

还有一种redission加锁

@Autowired
private RedissonClient redissonClient
/*
* 这里只演示可重入锁,其他锁详情请查看官方文档
*/
// 获取redisson锁对象
RLock lock = redissonClient.getLock("anyLock");

//(1) 最常见的使用方法
lock.lock();

//(2) 加锁以后10秒钟自动解锁
// 无需调用unlock方法手动解锁
lock.lock(10, TimeUnit.SECONDS);
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       lock.unlock();
   }
}

// (3)Redisson同时还为分布式锁提供了异步执行的相关方法
lock.lockAsync();
lock.lockAsync(10, TimeUnit.SECONDS);
Future<Boolean> res = lock.tryLockAsync(100, 10, TimeUnit.SECONDS);
<dependency>
    <groupId>org.redisson</groupId>
     <artifactId>redisson</artifactId>
     <version>3.13.3</version>
 </dependency>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值