MENU

RedisLock 分布式锁

April 1, 2022 • 技术阅读设置

代码是应该是21年南哥(好基友)发给我让我学习的,一直没有认真的去读这块代码,最近正好要用到Redis锁,便又向南哥请教理解了下。

代码

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;


@Component
@Slf4j
public class RedisLock {

    /**
     * 锁超时时间
     */
    private final static long LOCK_OUT_TIME_INTERVAL = 30 * 60 * 1000;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 加锁
     *
     * @param key
     * @param lockTime
     * @return
     */
    public synchronized boolean lock(String key, long lockTime) {
        try {
            // 锁超时时间
            String lockOutTime = String.valueOf(lockTime + LOCK_OUT_TIME_INTERVAL);
            ValueOperations<String, String> stringStringValueOperations = redisTemplate.opsForValue();
            // 尝试加锁
            if (stringStringValueOperations.setIfAbsent(key, lockOutTime)) {
                log.info("Lock success; key: {}, lockOutTime: {}", key, lockOutTime);
                return true;
            }
            // 未取到锁
            // 验证锁超时 预防死锁
            String lastLockOutTime = stringStringValueOperations.get(key);
            // 锁不为空 且 小于当前时间
            if (StringUtils.isNotBlank(lastLockOutTime) && Long.parseLong(lastLockOutTime) < lockTime) {
                stringStringValueOperations.set(key, lockOutTime);
                log.info("Verify the deadlock; Lock success; key: {}, lockOutTime: {}, lastLockOutTime: {}", key, lockOutTime, lastLockOutTime);
                return true;
            }
            log.warn("Lock failed; key: {}, lockOutTime: {}", key, lockOutTime);
        } catch (Exception e) {
            log.error("Lock failed; key: {}", key);
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 解锁
     *
     * @param key
     * @param lockTime
     */
    public void unlock(String key, long lockTime) {
        ValueOperations<String, String> stringStringValueOperations = redisTemplate.opsForValue();
        String lockOutTime = stringStringValueOperations.get(key);
        if (StringUtils.isNotBlank(lockOutTime)) {
            if (lockOutTime.equals(String.valueOf(lockTime + LOCK_OUT_TIME_INTERVAL))) {
                stringStringValueOperations.getOperations().delete(key);
                log.info("Unlock success; key: {}, lockOutTime: {}", key, lockOutTime);
            } else {
                log.warn("Unlock failed; The lock time does not match; key: {}, lockOutTime: {}", key, lockOutTime);
            }
        } else {
            log.warn("Unlock failed; The lock not found; key: {}, lockOutTime: {}", key, lockOutTime);
        }
    }
}

说明

lock及unlock方法的参数lockTime:保证在锁的有效期内,只有加锁的人才能进行解锁。
lock方法代码40行,主动式处理死锁:在锁的有效期外,尝试加锁时会自动清理上次的死锁。

最后

哪天闲了试试通过自定义注解搭配切面进行加解锁的操作,在想要不要支持动态可传的超时时间。

Last Modified: November 17, 2023