redis分布式锁实现

import lombok.extern.slf4j.Slf4j;

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;

@Slf4j
public class RedisLock {

    //加锁超时时间,单位毫秒, 即:加锁时间内执行完操作,如果未完成会有并发现象
    private long lockTimeout;
    private RedisTemplate redisTemplate;
    public RedisLock(RedisTemplate redisTemplate, long timeout) {
        this.redisTemplate = redisTemplate;
        this.lockTimeout = timeout;
    }

    private boolean setNX(final String key, final String value) {
        Object obj = null;
        try {
            obj = redisTemplate.execute(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    StringRedisSerializer serializer = new StringRedisSerializer();
                    Boolean success = connection.setNX(serializer.serialize(key), serializer.serialize(value));
                    connection.close();
                    return success;
                }
            });
        } catch (Exception e) {
            log.error("setNX redis error, key : {}", key);
        }
        return obj != null ? (Boolean) obj : false;
    }

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

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

    private boolean del(final String lockKey) {
        try {
            redisTemplate.execute(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    StringRedisSerializer serializer = new StringRedisSerializer();
                    Long del = connection.del(serializer.serialize(lockKey));
                    connection.close();
                    return del;
                }
            });
            return true;
        } catch (Exception e) {
            log.error("del redis error, key : {}", lockKey);
        }
        return false;
    }

    private boolean expire(final String lockKey, final Long expireTime) {
        Object obj = null;
        try {
          obj =  redisTemplate.execute(new RedisCallback<Boolean>() {
                @Override
                public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                    StringRedisSerializer serializer = new StringRedisSerializer();
                    boolean bool = connection.expire(serializer.serialize(lockKey), expireTime);
                    connection.close();
                    return bool;
                }
            });
            return (Boolean)obj;
        } catch (Exception e) {
            log.error("expire redis error, key : {}", lockKey);
        }
        return false;
    }

    /**
     * 加锁
     * 取到锁加锁,取不到锁就返回
     *
     * @param lockKey
     * @param threadName
     * @return
     */
    public synchronized long lock(String lockKey, String threadName) {
        log.debug(threadName + "开始执行加锁");
        //锁时间
        Long lock_timeout = currtTimeForRedis() + lockTimeout * 1000 + 1;
        if (setNX(lockKey, String.valueOf(lock_timeout))) {
            //如果加锁成功
            log.debug(threadName + "加锁成功+1");
            //设置超时时间,释放内存
            expire(lockKey, lockTimeout);
            return lock_timeout;
        } else {
            //获取redis里面的时间
            Object result = get(lockKey);
            Long currt_lock_timeout_str = result == null ? null : Long.parseLong(result.toString());
            //锁已经失效
            if (currt_lock_timeout_str != null && currt_lock_timeout_str < currtTimeForRedis()) {
                //判断是否为空,不为空时,说明已经失效,如果被其他线程设置了值,则第二个条件判断无法执行
                //获取上一个锁到期时间,并设置现在的锁到期时间
                Long old_lock_timeout_Str = Long.valueOf(getSet(lockKey, String.valueOf(lock_timeout)));
                if (old_lock_timeout_Str != null && old_lock_timeout_Str.equals(currt_lock_timeout_str)) {
                    //多线程运行时,多个线程签好都到了这里,但只有一个线程的设置值和当前值相同,它才有权利获取锁
                    log.debug(threadName + "加锁成功+2");
                    //设置超时间,释放内存
                    expire(lockKey, lockTimeout);
                    //返回加锁时间
                    return lock_timeout;
                }
            }
        }
        return -1;
    }
    /**
     * 解锁
     *
     * @param lockKey
     * @param lockValue
     * @param threadName
     */
    public synchronized void unlock(String lockKey, long lockValue, String threadName) {
        log.debug(threadName + "执行解锁==========");//正常直接删除 如果异常关闭判断加锁会判断过期时间
        //获取redis中设置的时间
        String result = get(lockKey);
        log.debug("获取到的locakValue为:[{}],当前线程为:[{}]", result, threadName);
        Long currt_lock_timeout_str = result == null ? null : Long.valueOf(result);
        //如果是加锁者,则删除锁, 如果不是,则等待自动过期,重新竞争加锁
        if (currt_lock_timeout_str != null && currt_lock_timeout_str == lockValue) {
            del(lockKey);
            log.debug(threadName + "解锁成功------------------");
        }
    }
    /**
     * 多服务器集群,使用下面的方法,代替System.currentTimeMillis(),获取redis时间,避免多服务的时间不一致问题!!!
     *
     * @return
     */
    public long currtTimeForRedis() {
        Object obj = redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                return redisConnection.time();
            }
        });
        return obj == null ? -1 : Long.parseLong(obj.toString());
    }
}

转自:https://gitee.com/autShadow/codes/dwc7zv0aeyfug6x3hl29o68

redis教程:http://www.runoob.com/redis/redis-data-types.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值