分布式锁的实现

83 阅读2分钟

说明: 为了脚手架的的实现,此处不使用Redission来直接实现,完全手动实现一个抢占式的分布式锁

使用场景:

1)任务调度(集群环境下,一个服务的多个实例的任务不想同一时间都进行执行)

2)并发修改相关(操作同一个数据)

1. 添加字符串校验依赖

<!--字符串校验-->
<dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.6</version>
</dependency>

2. 自定义异常--运行时异常类ShareLockException

image.png

package com.ssm.redis.exception;

public class ShareLockException extends RuntimeException {

    //自定义异常

    public ShareLockException(String message) {
        super(message);
    }
}

3. 实现: Redis分布式抢占锁RedisShareLockUtil

package com.ssm.redis.util;

import com.ssm.redis.exception.ShareLockException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;

@Component
public class RedisShareRedLock {

    @Autowired
    private RedisUtil redisUtil;

    private Long OUT_TIME = 1000L; //10s
    /**
     * @param lockKey 锁的key
     * @param requestId 锁的标识,类似线程的threadID, 删锁时判断是否为自己的锁
     *                  防止误删(业务1未执行完,锁自动删除,业务二获取锁,此时业务1执行完,删锁时把业务2的锁删了)
     * @param time
     * @return
     */
    public boolean lock(String lockKey, String requestId, Long time) {
        //1.参数校验
        if(StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || time <= 0) {
            //commons.lang包中的isBlank会判断null和空字符串两种情况
            throw new ShareLockException("分布式锁-加锁参数异常"); // 自定义异常
        }

        //2.加锁可自旋(一定时间内可重复获取锁)
        long currentTime = System.currentTimeMillis(); //获取当前毫秒数
        long outTime = currentTime + OUT_TIME;
        Boolean result = false;

        while(currentTime < outTime) {
            //3.利用redis的setnx设置锁
            result = redisUtil.setNx(lockKey, requestId, time, TimeUnit.MILLISECONDS);

            if(result) { //为true获取锁成功
                return result;
            }

            //获取失败,等待100ms重新尝试
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            currentTime = System.currentTimeMillis();
        }

        return result;
    }

    public boolean unLock(String lockKey, String requestId) {
        if(StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId)) {
            throw new ShareLockException("分布式锁-解锁参数异常");
        }

        try {
            //获取当前锁的值
            String value = redisUtil.get(lockKey);
            if(requestId.equals(value)) {
                //是自己的锁,删除
                boolean del = redisUtil.del(lockKey);
                return true;
            }
        } catch (Exception e) {
            //补日志
        }

        return false;
    }

    public boolean tryLock(String lockKey, String requestId, Long time) {
        //只是尝试获取锁,没有自旋操作

        if(StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || time <= 0) {
            throw new ShareLockException("分布式锁-加锁参数异常");
        }

        return redisUtil.setNx(lockKey, requestId, time, TimeUnit.MILLISECONDS);
    }

}