Java实现分布式锁-基于redis

该文章展示了一个基于Redis的分布式锁实现,利用RedisTemplate和Lua脚本进行加锁与解锁操作。DistributedLock类包含了获取与释放锁的方法,支持任务运行超时和锁等待超时的处理,确保了并发环境下的正确性。
摘要由CSDN通过智能技术生成

导入依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>20.0</version>
</dependency>
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
    <version>5.0.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

代码实现

import com.google.common.collect.Lists;
import org.apache.commons.lang3.BooleanUtils;
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.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.LockSupport;

/**
 * 分布式锁
 */
@Component
public class DistributedLock {

    public static final String LOCK_PREFIX = "member_lock:";

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private final RedisScript<Boolean> lockScript = new DefaultRedisScript<>(
            "if redis.call('exists',KEYS[1]) == 1 then " +
                    " return false; " +
                    "end " +
                    "if(redis.call('set',KEYS[1],ARGV[1],'EX',ARGV[2],'NX')) " +
                    "then " +
                    " return true;" +
                    "else " +
                    " return false;" +
                    "end", Boolean.class);
    private final RedisScript<Boolean> unlockScript = new DefaultRedisScript<>(
            "if redis.call('get',KEYS[1]) == ARGV[1] " +
                    "then" +
                    " redis.call('del',KEYS[1]) " +
                    "end", Boolean.class);

    /**
     * 获取指定任务的锁并运行指定的任务
     *
     * @param task            任务名称
     * @param runnable        任务内容
     * @param acquireInterval 锁请求的间隔时长
     * @param waitTimeout     锁等待超时时长,负数表示永不超时
     * @param taskTimeout     任务运行超时时长(锁占用时长)
     * @param timeUnit        时间单位
     * @throws TimeoutException 锁等待超时时抛出此异常
     */
    public void run(String task, Runnable runnable, long acquireInterval
            , long waitTimeout, long taskTimeout, TimeUnit timeUnit) throws TimeoutException {
        run(task, () -> {
            runnable.run();
            return null;
        }, acquireInterval, waitTimeout, taskTimeout, timeUnit);
    }

    /**
     * 获取指定任务的锁并运行指定的任务
     *
     * @param task            任务名称
     * @param callable        任务内容
     * @param acquireInterval 锁请求的间隔时长,负数表示无间隔时长
     * @param waitTimeout     锁等待超时时长,负数表示永不超时
     * @param taskTimeout     任务运行超时时长(锁占用时长)
     * @param timeUnit        时间单位
     * @return 任务返回值
     * @throws TimeoutException 锁等待超时时抛出此异常
     */
    public <V> V run(String task, Callable<V> callable, long acquireInterval
            , long waitTimeout, long taskTimeout, TimeUnit timeUnit) throws TimeoutException {
        if (StringUtils.isBlank(task)) {
            throw new IllegalArgumentException("任务名称不能为空");
        }
        if (callable == null) {
            throw new IllegalArgumentException("任务执行内容不能为Null");
        }
        if (taskTimeout < 1) {
            throw new IllegalArgumentException("任务运行时长不能小于1");
        }
        if (timeUnit == null) {
            throw new IllegalArgumentException("时间单位不能为Null");
        }
        String lockKey = LOCK_PREFIX + task;
        String lockValue = UUID.randomUUID().toString();
        List<String> keys = Lists.newArrayList(lockKey);
        Boolean locked = Boolean.FALSE;
        long waitTimeoutMillis = System.currentTimeMillis() + timeUnit.toMillis(waitTimeout);
        try {
            do {
                locked = redisTemplate.execute(lockScript, keys,
                        lockValue, timeUnit.toSeconds(taskTimeout));
                if (BooleanUtils.isNotTrue(locked)) {
                    if (waitTimeout >= 0 && System.currentTimeMillis() >= waitTimeoutMillis) {
                        throw new TimeoutException("Lock wait timeout:" + task);
                    }
                    if (acquireInterval > 0) {
                        LockSupport.parkNanos(timeUnit.toNanos(acquireInterval));
                    }
                }
            } while (BooleanUtils.isNotTrue(locked));
            return callable.call();
        } catch (TimeoutException e) {
            throw e;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            if (BooleanUtils.isTrue(locked)) {
                redisTemplate.execute(unlockScript, keys, lockValue);
            }
        }
    }

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值