欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

springboot设置redis全局锁

程序员文章站 2022-12-20 13:31:55
代码环境:spring boot项目如何用redis设置一个全局锁,如何保证在服务器不可用时,锁可以安全释放?废话不多说,上代码,如遇到任何问题,可随时留言import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.dao.DataAccessException;import org.springframework.data.redis.connection.RedisConnection;....
代码环境:spring boot项目
如何用redis设置一个全局锁,如何保证在服务器不可用时,锁可以安全释放?废话不多说,上代码,如遇到任何问题,可随时留言
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.lang.Nullable;

/**
 * Created by jiangtenglong no 2020/6/3 .
 */
public class RedisLock {

    Logger logger = LoggerFactory.getLogger(RedisLock.class);

    //默认锁的等待时间
    private int waitTime = 10 * 1000;

    //每次循环所等待的时间
    private static final int sleepTime = 100;

    /**
     * 锁超时时间,防止线程在入锁以后,无限的执行等待,设置为30秒
     */
    private int expireTimes = 30 * 1000;


    private boolean locked;

    private String lockKey;

    //锁定的时间
    private long expiresLock;

    RedisTemplate redisTemplate;

    public RedisLock(RedisTemplate redisTemplate,RedisLockEnum redisLockEnum,final String key){
        this.redisTemplate = redisTemplate;
        this.lockKey = String.format(redisLockEnum.getKeyHead(),key);
    }

    public RedisLock(RedisTemplate redisTemplate,RedisLockEnum redisLockEnum,final String key,int waitTime){
        this.redisTemplate = redisTemplate;
        this.lockKey = String.format(redisLockEnum.getKeyHead(),key);
        this.waitTime = waitTime;
    }

    public RedisLock(RedisTemplate redisTemplate,RedisLockEnum redisLockEnum,final String key,int waitTime,int expireTimes){
        this.redisTemplate = redisTemplate;
        this.lockKey = String.format(redisLockEnum.getKeyHead(),key);
        this.waitTime = waitTime;
        this.expireTimes = expireTimes;
    }

    public synchronized boolean lock() throws InterruptedException {
        int times = waitTime;
        while (times>0){
            long expires = System.currentTimeMillis() + expireTimes + 1;
            String expiresStr = String.valueOf(expires); //锁到期时间
            if (this.setNX(lockKey, expiresStr)) {//尝试抢锁
                logger.info("获取锁成功");
                locked = true;
                expiresLock = expires;
                return true;
            }
            logger.info("获取锁失败,等待获取锁");
            //没抢到锁,尝试获取锁对应的时间,看是不是获取锁的那个线程完蛋了
            String currentValueStr = this.get(lockKey); //redis里的时间
            if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {//锁的时间已经超时了
                String oldValueStr = this.getSet(lockKey, expiresStr);
                if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                    logger.info("上一个线程超时,本次获取锁成功");
                    expiresLock = expires;
                    locked = true;
                    return true;
                }
            }
            times -= sleepTime;
            Thread.sleep(sleepTime);
        }
        return false;

    }




    //进行setNx获取锁
    private boolean setNX(final String key,final String value){
        Object object = null;
        try {
            object = redisTemplate.execute(new RedisCallback<Object>() {
                @Nullable
                @Override
                public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                    StringRedisSerializer serializer = new StringRedisSerializer();
                    boolean successLock = redisConnection.setNX(serializer.serialize(key), serializer.serialize(value));
                    redisConnection.close();
                    return successLock;
                }
            });
        }catch (Exception e){
            logger.error("redis 锁异常 key:"+key,e);
        }
        return object != null ? (Boolean) object : false;
    }

    private String get(final String key) {
        Object obj = null;
        try {
            obj = redisTemplate.execute(new RedisCallback<Object>() {
                @Nullable
                @Override
                public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                    StringRedisSerializer serializer = new StringRedisSerializer();
                    byte[] data = redisConnection.get(serializer.serialize(key));
                    redisConnection.close();
                    if (data == null) {
                        return null;
                    }
                    return serializer.deserialize(data);
                }
            });
        } catch (Exception e) {
            logger.error("redis 异常 key:"+key,e);
        }
        return obj != null ? obj.toString() : null;
    }

    private String getSet(final String key, final String value) {
        Object object = null;
        try {
            object = redisTemplate.execute(new RedisCallback<Object>() {
                @Nullable
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    StringRedisSerializer serializer = new StringRedisSerializer();
                    byte[] ret = connection.getSet(serializer.serialize(key), serializer.serialize(value));
                    connection.close();
                    return serializer.deserialize(ret);
                }
            });
        } catch (Exception e) {
            logger.error("redis 异常 key:"+key,e);
        }
        return object != null ? (String) object : null;
    }

    private void delete(final String key){
        try {
            redisTemplate.execute(new RedisCallback<Object>() {
                @Nullable
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    StringRedisSerializer serializer = new StringRedisSerializer();
                    Long ret = connection.del(serializer.serialize(key));
                    connection.close();
                    return ret;
                }
            });
        } catch (Exception e) {
            logger.error("redis 异常 key:"+key,e);
        }
    }


    public synchronized void unlock() {
        logger.info("尝试删除锁状态");
        String currentValueStr = this.get(lockKey); //redis里的时间
        if (locked) {
            logger.info("准备判定是否还为自己的锁"+currentValueStr+"——"+expiresLock);
            if (currentValueStr != null && Long.parseLong(currentValueStr) == expiresLock) {//锁的时间是我自己设置的那个时间,才可以解锁,否则可能是已经超时被别的线程拿到了锁,此时,我不能删除
//                redisTemplate.delete(lockKey);
                this.delete(lockKey);
                currentValueStr = this.get(lockKey); //redis里的时间
                logger.info("查看是否删除成功:"+currentValueStr);
                locked = false;
            }
        }
    }

}
/**
 * redis锁的枚举维护类
 * Created by jiangtenglong no 2020/6/3 .
 */
public enum RedisLockEnum {


    LOCK_KEY("LOCK_KEY_%s","锁枚举头");

    RedisLockEnum(String keyHead,String describe){
        this.keyHead=keyHead;
        this.describe=describe;
    };

    private String keyHead;
    private String describe;

    public String getKeyHead() {
        return keyHead;
    }

    public void setKeyHead(String keyHead) {
        this.keyHead = keyHead;
    }

    public String getDescribe() {
        return describe;
    }

    public void setDescribe(String describe) {
        this.describe = describe;
    }
}

使用方法

 

RedisLock lock = new RedisLock(redisTemplate, RedisLockEnum.LOCK_KEY,"锁的key");
    try{
    if(lock.lock()){
        System.out.println("获取锁成功");
    }
}catch (Exception e){
    //异常处理
}finally {
    //锁释放
    lock.unlock();
}

本文地址:https://blog.csdn.net/debushiwo/article/details/107347076