分布式锁的实现

场景:由于我们是使用springcloud中的分布式场景,在高并发的情况下,会对其进行加锁操作,以下就是使用setNx来对其进行加锁操作,
pom文件依赖:

<!--spring 测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!-- 开启web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--springboot整合redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

下面是对配置文件中的内容:

server:
  port: 8084

spring:
  redis:
    host: localhost
    port: 6379 #redis中的端口号

具体的代码实现流程:全局锁类:分布式锁类的设计

/**
 * @Author: Jason
 * @Create: 2020/10/11  16:37
 * @Description 全局锁类 主要有名称与value值
 */
@Data
public class Lock {

    private String name;
    private String value;

    public Lock(String name, String value){
        this.name = name;
        this.value = value;
    }
}


分布式锁类的设计:

@Component
public class DistributedLockHandler {
    private static final Logger logger = LoggerFactory.getLogger(DistributedLockHandler.class);
    private final static long LOCK_EXPIRE = 30 * 1000L; //单个业务持有锁的时间
    private final static long LOCK_TRY_INTERVAL = 30L; //默认30ms一次
    private final static long LOCK_TRY_TIMEOUT = 20 * 1000L; //默认尝试20s

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 尝试获取全局锁
     * @param lock 锁的名称
     * @return  true 获取成功,false获取失败
     */
    public boolean tryLock(Lock lock){
        return getLock(lock, LOCK_TRY_TIMEOUT, LOCK_TRY_INTERVAL, LOCK_EXPIRE);
    }

    /**
     *尝试获取全局锁
     * @param lock      锁的名称
     * @param timeout   获取锁的超时时间,单位ms
     * @return      true 获取成功, false 获取失败
     */
    public boolean  tryLock(Lock lock, long timeout){
        return getLock(lock, timeout, LOCK_TRY_INTERVAL, LOCK_EXPIRE);
    }

    /**
     * 尝试获取全局锁
     * @param lock     锁的名称
     * @param timeout  获取锁的超时时间
     * @param tryInterval 获取成功  true 代表成功  false 代表失败
     * @return
     */
    public boolean tryLock(Lock lock, long timeout, long tryInterval){
        return getLock(lock, timeout, tryInterval, LOCK_EXPIRE);
    }

    /**
     * 尝试获取全局锁
     * @param lock           锁的名称
     * @param timeout        获取锁的超时时间
     * @param tryInterval    尝试间隔次数
     * @param lockExpireTime 锁的过期时间
     * @return
     */
    public boolean tryLock(Lock lock, long timeout, long tryInterval, long lockExpireTime){
        return getLock(lock, timeout, tryInterval, LOCK_EXPIRE);
    }
    /**
     * 操作redis获取全局锁
     * @param lock         锁的名称
     * @param timeout      获取的超时时间
     * @param tryInterval   多少ms尝试一次
     * @param lockExpireTime 获取成功后锁的过期时间
     * @return    true 获取成功 false 获取失败
     * @throws InterruptedException
     * 其原理是:在判断是否有key的基础上,进行循环,只有当不存在key才对其进行加锁操作,其他不对其进行任何操作
     */
    public boolean  getLock(Lock lock, long timeout, long tryInterval, long lockExpireTime) {
        if(StringUtils.isEmpty(lock.getName()) || StringUtils.isEmpty(lock.getValue())){
            return false;
        }

        long startTime = System.currentTimeMillis();
        do{
            if (!stringRedisTemplate.hasKey(lock.getName())){
                ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
                ops.set(lock.getName(),lock.getValue(), lockExpireTime);
                return true;
            }else{//存在锁
                logger.debug("lock is exist");
            }
            //尝试超过设定值之后直接跳出循环
            if (System.currentTimeMillis() - startTime > timeout){
                return false;
            }
            try {
                Thread.sleep(tryInterval);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        while (stringRedisTemplate.hasKey(lock.getName()));
        return false;
    }

    /**
     * 释放锁方法
     * @param lock
     */
    public void releaseLock(Lock lock){
        if (!StringUtils.isEmpty(lock.getName())){
            stringRedisTemplate.delete(lock.getName());

        }
    }

但是这种方法redis不推荐,因为这是一个耗时的操作影响性能
redis推荐使用redission锁,具体的代码流程:
首先是定义好一个获取锁之后的接口 - 然后就是定义好分布式锁的接口
pom文件是对前面进行改造之后的只是添加一个pom文件:

<!--redisson分布式锁-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.7.0</version>
        </dependency>```
代码分析:这种感觉更像一种设计模式
/**
 * @Author: Jason
 * @Create: 2020/10/12  20:13
 * @Description 获取锁之后的操作
 */
public interface AquiredLockWorker<T> {
    T invokeAfterLockAquire() throws Exception;
}


/**
 * @Author: Jason
 * @Create: 2020/10/12  20:14
 * @Description 分布式锁的管理类
 */
public interface DistributedLocker {

    /**
     * 获取锁
     * @param resourceName   锁的名称
     * @param worker         获取锁后的处理类
     * @param <T>
     * @return    处理完具体的业务逻辑要返回的数据
     * @throws Exception
     */
    <T> T lock(String resourceName, AquiredLockWorker<T> worker) throws Exception;

    <T> T lock(String resourceName, AquiredLockWorker<T> worker , int lockTime) throws Exception;
}


@Component
public class RedisLock implements DistributedLocker {

    private final static String LOCKER_PREFIX = "lock";

    @Autowired
    RedissonConnector redisConnection;

    @Override
    public <T> T lock(String resourceName, AquiredLockWorker<T> worker) throws Exception {
        return lock(resourceName, worker, 100);
    }

    /**
     *感觉更像一种设计模式一样
     * @param resourceName
     * @param worker
     * @param lockTime
     * @param <T>
     * @return
     * @throws Exception
     */
    @Override
    public <T> T lock(String resourceName, AquiredLockWorker<T> worker, int lockTime) throws Exception {
        RedissonClient  redisson = redisConnection.getRedissonClient();
        RLock lock = redisson.getLock(LOCKER_PREFIX + resourceName);
        //等待100秒之后,自动释放锁
        boolean success = lock.tryLock(100, lockTime, TimeUnit.SECONDS);
        if(success){
            try{
                return worker.invokeAfterLockAquire();
            }finally {
                lock.unlock();
            }
        }
            throw new UnableToAquireLockException();
    }
}


/**
 * @Author: Jason
 * @Create: 2020/10/12  20:18
 * @Description 异常类
 */
public class UnableToAquireLockException extends RuntimeException{

    public UnableToAquireLockException(){};

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

    public UnableToAquireLockException(String message, Throwable cause){
        super(message,cause);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值