sping boot 中使用 redis 分布式锁

本文介绍了一种基于Redis的分布式锁实现方法,通过使用Lua脚本确保锁的安全释放,支持阻塞与非阻塞锁获取,提供了锁持有时间的自定义,并详细展示了如何构建和使用RedisLock类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RedisLock

package com.lybaby.lock;

import cn.hutool.core.lang.Assert;
import com.google.common.base.Function;
import com.lybaby.lock.JedisTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;

import java.util.Collections;


public class RedisLock {
    private static Logger logger = LoggerFactory.getLogger(RedisLock.class);

    private static final String LOCK_MSG = "OK";

    private static final Long UNLOCK_MSG = 1L;

    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";


    private String lockPrefix;

    /**
     * 循环等待 每次 睡眠时间
     */
    private int sleepMillis;

    /**
     * 锁持有的时间
     */
    private int lockHoldMillis;


    private JedisTemplate jedisTemplate;

    private RedisScript redisScript;

    private RedisLock(Builder builder) {
        this.jedisTemplate = builder.jedisTemplate;
        this.lockPrefix = builder.lockPrefix;
        this.sleepMillis = builder.sleepMillis;
        this.redisScript = builder.redisScript;
        this.lockHoldMillis = builder.lockHoldMillis;
    }


    /**
     * blocking lock
     *
     * @param key
     * @param request
     */
    public void lock(String key, String request) throws InterruptedException {
        //get connection
        String result;
        for (; ; ) {
            result = set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, lockHoldMillis);
            if (LOCK_MSG.equals(result)) {
                break;
            }
            Thread.sleep(sleepMillis);
        }

    }

    /**
     * Non-blocking lock
     *
     * @param key     lock business type
     * @param request value
     * @return true lock success
     * false lock fail
     */
    public boolean tryLock(final String key, final String request) {
        String result = set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, lockHoldMillis);
        if (LOCK_MSG.equals(result)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * blocking lock,custom time
     *
     * @param key
     * @param request
     * @param timeoutMs 等待获取锁超时时间 (毫秒)
     * @return
     * @throws InterruptedException
     */
    public boolean tryLock(String key, String request, int timeoutMs) throws InterruptedException {
        String result;
        for (; ; ) {
            result = set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, lockHoldMillis);
            if (LOCK_MSG.equals(result)) {
                return true;
            }
            timeoutMs -= sleepMillis;
            if (timeoutMs < 0) {
                return false;
            }
            Thread.sleep(sleepMillis);
        }
    }


    /**
     * Non-blocking lock
     *
     * @param key            lock business type
     * @param lockHoldMillis 持有锁的时间(设置redis key 的过期时间)
     * @param request        value
     * @return true lock success
     * false lock fail
     */
    public boolean tryLock(String key, int lockHoldMillis, String request) {
        //get connection
        String result;
        result = set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, lockHoldMillis);
        if (LOCK_MSG.equals(result)) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * unlock
     *
     * @param key
     * @param request request must be the same as lock request
     * @return
     */
    public boolean unlock(String key, String request) {
        //lua script
        Object result = null;
        result = redisScript.eval("lock.lua", Collections.singletonList(lockPrefix + key), Collections.singletonList(request));

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

    /**
     * jedis 方法
     *
     * @param key
     * @param value
     * @param nxxx
     * @param expx
     * @param time
     * @return
     */
    private String set(final String key, final String value, final String nxxx, final String expx,
                       final int time) {
        return jedisTemplate.execute(new Function<Jedis, String>() {
            @Override
            public String apply(Jedis jedis) {
                return jedis.set(key, value, nxxx, expx, time);
            }
        });

    }

    public static class Builder {
        private static final String DEFAULT_LOCK_PREFIX = "lock_";
        /**
         * default sleep time 100 毫秒
         */
        private static final int DEFAULT_SLEEP_MILLIS = 100;

        private JedisTemplate jedisTemplate;
        private RedisScript redisScript;

        private String lockPrefix = DEFAULT_LOCK_PREFIX;
        private int sleepMillis = DEFAULT_SLEEP_MILLIS;
        private int lockHoldMillis = 60 * 1000;

        public Builder(JedisTemplate jedisTemplate, RedisScript redisScript) {
            this.jedisTemplate = jedisTemplate;
            this.redisScript = redisScript;
        }

        public Builder lockPrefix(String lockPrefix) {
            this.lockPrefix = lockPrefix;
            return this;
        }

        public Builder sleepMillis(int sleepMillis) {
            this.sleepMillis = sleepMillis;
            return this;
        }

        public Builder lockHoldMillis(int lockHoldMillis) {
            this.lockHoldMillis = lockHoldMillis;
            return this;
        }

        public RedisLock build() {
            Assert.notNull(this.jedisTemplate);
            Assert.notNull(this.redisScript);

            return new RedisLock(this);
        }

    }

    public static void main(String[] args) {
        //需要初始化
        JedisTemplate jedisTemplate = null;
        RedisScript redisScript = null;

        RedisLock redisLock = new RedisLock.Builder(jedisTemplate, redisScript)
                .lockPrefix("disLock_")
                .sleepMillis(100)
                .build();
    }
}

lock.lua

if redis.call('get', KEYS[1]) == ARGV[1] then
    return redis.call('del', KEYS[1])
else
    return 0
end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值