使用RedisTemplate实现简易的分布式锁(仅供参考)

package com.*.lock;

import lombok.extern.log4j.Log4j2;
import org.nutz.lang.Strings;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.Jedis;

import java.util.Collections;
import java.util.Objects;
import java.util.UUID;

/**
 * @author yangxing
 * @since 2018-09-14 10:19
 **/
@Log4j2
public class RedisLock {
    // 锁标志
    public static final String PREFIX = "lock_";
    // SET_IF_NOT_EXIST 标志,表示如果当key不存在时进行set操作,否则不处理
    private static final String NX = "NX";
    // 过期时间标志, EX 表示以秒作为单位
    private static final String EX = "EX";
    // 锁成功标志
    private static final String LOCK_SUCCESS = "OK";
    // 解锁成功标志
    private static final long DEL_SUCCESS = 1L;
    // 默认过期时间
    private static final int DEFAULT_EXPIRE_TIME = 60;
    // 默认重试次数
    private static final int DEFAULT_RETRY_TIME = 3;
    // 默认重试暂停时间
    private static final int DEFAULT_INTERNAL_TIME = 100;
    // 解锁时的 lua语言Script
    private static final String DEL_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

    private RedisTemplate<String, Object> redisTemplate;
    // 过期时间
    private int expireSeconds;
    // 锁重试次数
    private int retryTimes;
    // 锁重试间隔
    private int internalTime;
    // 加锁者标志, 解锁时只有与加锁时的值一致才允许解锁
    private final String lockId;

    private RedisLock(RedisTemplate<String, Object> redisTemplate) {
        this(redisTemplate, DEFAULT_EXPIRE_TIME, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME);
    }

    private RedisLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds, int retryTimes, int internalTime) {
        this.redisTemplate = redisTemplate;
        this.expireSeconds = expireSeconds;
        this.retryTimes = retryTimes;
        this.internalTime = internalTime;
        this.lockId = UUID.randomUUID().toString();
    }

    public boolean lock(String key) {
        if (Strings.isBlank(key)) {
            return false;
        }

        final String fullKey = PREFIX + key;

        log.debug("尝试获取锁, 锁Key:{}, 锁标识:{}", fullKey, lockId);
        for (int i = 0; i < retryTimes; i++) {
            log.debug("第 {} 次尝试获取锁", i + 1);
            String status = redisTemplate.execute((RedisCallback<String>) connection -> {
                try {
                    Jedis jedis = (Jedis) connection.getNativeConnection();
                    return jedis.set(fullKey, lockId, NX, EX, expireSeconds);
                } catch (Exception e) {
                    return "ERROR";
                }
            });

            if (LOCK_SUCCESS.equalsIgnoreCase(status)) {
                log.debug("获取锁成功, 锁Key:{}, 锁标识:{}", fullKey, lockId);
                return true;
            }

            try {
                Thread.sleep(internalTime);
            } catch (InterruptedException e) {
                break;
            }
        }

        log.debug("尝试获取锁 {} 次后失败, 锁Key:{}", retryTimes, fullKey);
        return false;
    }

    public boolean unlock(String key) {
        if (Strings.isBlank(key)) {
            return false;
        }

        final String fullKey = PREFIX + key;
        log.debug("尝试解锁, 锁Key:{}, 锁标识:{}", fullKey, lockId);
        Object value = redisTemplate.execute((RedisCallback<Object>) connection -> {
            try {
                Jedis jedis = (Jedis) connection.getNativeConnection();
                return jedis.eval(DEL_SCRIPT,
                        Collections.singletonList(fullKey), Collections.singletonList(lockId));
            } catch (Exception e) {
                return -1L;
            }
        });

        boolean isUnlock = Objects.nonNull(value) && value.equals(DEL_SUCCESS);

        if (isUnlock) {
            log.debug("解锁成功, 锁Key:{}", fullKey);
        }

        return isUnlock;
    }

    public String getLockId() {
        return lockId;
    }

    public static RedisLock getLock(RedisTemplate<String, Object> redisTemplate) {
        return new RedisLock(redisTemplate);
    }

    public static RedisLock getLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds) {
        return new RedisLock(redisTemplate, expireSeconds, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME);
    }

    public static RedisLock getLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds, int retryTimes, int internalTime) {
        return new RedisLock(redisTemplate, expireSeconds, retryTimes, internalTime);
    }
}

你可能感兴趣的:(Java,redis)