redis 分布式锁

  • Post author:
  • Post category:其他


package cn.admin.srv.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Objects;

@Component
@Slf4j
public class CommonRedisHelper {
    public static final String LOCK_PREFIX = "redis_lock";
    public static final int LOCK_EXPIRE = 10 * 1000; // 锁过期时间ms

    public static final int LOCK_EXPIRE_WAIT = 100; // 获取锁失败后的等待时间ms,等待时间过去后再次尝试获取

    private static final int LOCK_TIMEOUT = 5 * 1000; //锁等待时间,ms

    @Autowired
    RedisTemplate redisTemplate;


    public boolean lock(String key){
        log.info("lock invoke, key = {}", key);
        return (Boolean) redisTemplate.execute((RedisCallback) connection -> {
            //获取锁的超时时间,超过这个时间还没获取到锁,认为失败
            int timeoutMs = LOCK_TIMEOUT;
            while (timeoutMs >= 0) {
                //锁过期时间
                long expireAt = System.currentTimeMillis() + LOCK_EXPIRE + 1;
                Boolean acquire = connection.setNX(key.getBytes(), String.valueOf(expireAt).getBytes());

                //锁获取成功,直接返回
                if (acquire) {
                    log.info("lock acquire success, key = {}", key);
                    return true;
                }

                byte[] value = connection.get(key.getBytes());
                boolean isExpired = Objects.nonNull(value) && Long.parseLong(new String(value)) < System.currentTimeMillis();
                
                //如果当前锁过期 尝试通过更新锁时间 来获取锁
                if (isExpired) {
                    byte[] oldValue = connection.getSet(key.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE + 1).getBytes());
                    //如果锁的过期时间值 没有被其他线程修改 则获取锁成功
                    if (Objects.nonNull(oldValue) && Objects.equals(Long.parseLong(new String(value)), Long.parseLong(new String(oldValue)))) {
                        log.info("lock acquire success2 , key {}", key);
                        return true;
                    }
                    }
                //没有获取锁  等待时间 再去获取锁
                timeoutMs -= LOCK_EXPIRE_WAIT;
                try {
                    Thread.sleep(LOCK_EXPIRE_WAIT);
                } catch (InterruptedException e) {
                    log.error("lock error , key {}",key , e);
                }
            }
            //超时未获取到锁 则获取失败
            return false;
                });
    }

    /**
     * 删除锁
     *
     * @param key
     */
    public void deleteLock(String key) {
        redisTemplate.delete(key);
        log.info("删除lock key {}", key);
    }
}




版权声明:本文为weixin_58361879原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。