基于Redis的分布式锁

在集群多机器环境中,为了避免并发导致的冲突问题,就会使用到分布式锁,同一时刻,集群中只有获取到锁的节点能够执行处逻辑,如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Collections;

@Service("RedisSevice")
public class RedisSevice {

    @Autowired
    JedisPool jedisPool;

    private static final String LOCK_SUCCESS = "OK";
    
    //如果不存在key值就存入redis,如果存在返回失败,过期自动删除
    private static final String SET_IF_NOT_EXIST = "NX";
    
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    public boolean tryGetDistributedLock(String lockKey, String value, int expireTime) {
        Jedis jedis = jedisPool.getResource();
        //往redis写入key及value,如果key存在返回写入失败表示获取锁失败,key不存在返回写入成功表示获取锁成功
        String result = jedis.set(lockKey, value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

    private static final Long RELEASE_SUCCESS = 1L;

    public boolean releaseDistributedLock(String lockKey, String value) {
        Jedis jedis = jedisPool.getResource();
        /*当A节点获取锁之后执行过久,导致锁过期自动释放,B节点获取到了锁执行逻辑中,
        这时A执行完毕准备释放锁,如果发现value不是自己设置的,说明当前锁已经不是自己持有,则不能释放*/
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(value));

        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

}
@Component
public class DistributedLock{

    @Autowired
    RedisSevice redisSevice;
    
    //redis分布式锁key过期时间,防止执行时间过久或者宕机等异常原因导致锁无法释放
    private static final int EXPIRE_TIME = 30000;
    
    public void main(String[] args) {
        String lockKey = "key值";
        String lockValue = "value值,可用集群机器IP表明锁的持有者";
        boolean lockable = redisSevice.tryGetDistributedLock(lockKey, lockValue, EXPIRE_TIME);
        if (lockable) {
            try {
                //获取到锁,执行业务逻辑
            } finally {
                //执行结束释放锁
                redisSevice.releaseDistributedLock(lockKey, value);
            }
        }
    }
    
}

上述情况主要适用于单机redis,如果是集群,master数据未同步到salve节点时宕机,会导致锁丢失的问题,推荐使用Redisson分布式锁。