分布式锁详细简介说明

java问题王 Java每日一问 发布时间:2021-10-19 14:08:19 阅读数:15486 1
下文是笔者讲述分布式锁的相关简介说明,如下所示:

分布式锁简介

锁的功能:
用于解决多线程(或多进程)运行时,访问共享资源 错误或数据不一致问题的工具

为何需要分布式锁

分布式锁使用的两个场景:
  1. 避免不同节点重复相同的工作:比如用户执行了某个操作有可能不同节点会发送多封邮件
  2. 避免破坏数据的正确性:如果两个节点在同一条数据上同时进行操作,可能会造成数据错误或不一致的情况出现

Java中分布式锁的常见实现方式

分布式锁的实现条件:
     某个资源在同一时间只允许一个用户操作
下文将列举java中分布式锁常见的实现方式,如下所示:
  1. 基于 MySQL 中的锁:MySQL 本身有自带的悲观锁 for update 关键字,也可以自己实现悲观/乐观锁来达到目的
  2. 基于 Zookeeper 有序节点:Zookeeper 允许临时创建有序的子节点,这样客户端获取节点列表时,就能够当前子节点列表中的序号判断是否能够获得锁
  3. 基于 Redis 的单线程:由于 Redis 是单线程,所以命令会以串行的方式执行,并且本身提供了像 SETNX(set if not exists) 这样的指令,本身具有互斥性
下面笔者将重点讲述Redis分布式锁的相关使用说明

Redis分布式锁的问题

  
锁超时
  当有两台平行的服务A,B
当A服务在获取锁之后,突然A服务关闭,此时B服务就无法获取锁
此时就需为锁设置一个合理的超时时间,保证服务的可用性   
单点/多点问题
  当Redis 采用单机部署模式,则意味着当Redis出现故障,就会导致整个服务不可用
当我们采用主从模式部署,我们想象一个这样的场景:服务 A 申请到一把锁之后,此时作为主机的 Redis 宕机了
那么服务B在申请锁的时候就会从从机那里获取到这把锁,为了解决这个问题,Redis 作者提出了一种 RedLock 红锁的算法:

Redis分布式锁的实现

  Redis分布式锁使用SETNX(SET if Not eXists) 指令就是这样的一个操作
此命令只允许被一个客户端占有

Java Redis分布式的代码实现

  下文将使用Jedis来模拟实现,如下所示:
private static final String LOCK_SUCCESS = "OK";
private static final Long RELEASE_SUCCESS = 1L;
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";

@Override
public String acquire() {
    try {
        // 获取锁的超时时间,超过这个时间则放弃获取锁
        long end = System.currentTimeMillis() + acquireTimeout;
        // 随机生成一个 value
        String requireToken = UUID.randomUUID().toString();
        while (System.currentTimeMillis() < end) {
            String result = jedis
                .set(lockKey, requireToken, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
            if (LOCK_SUCCESS.equals(result)) {
                return requireToken;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    } catch (Exception e) {
        log.error("acquire lock due to error", e);
    }

    return null;
}

@Override
public boolean release(String identify) {
    if (identify == null) {
        return false;
    }

    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    Object result = new Object();
    try {
        result = jedis.eval(script, Collections.singletonlist(lockKey),
            Collections.singletonList(identify));
        if (RELEASE_SUCCESS.equals(result)) {
            log.info("release lock success, requestToken:{}", identify);
            return true;
        }
    } catch (Exception e) {
        log.error("release lock due to error", e);
    } finally {
        if (jedis != null) {
            jedis.close();
        }
    }

    log.info("release lock failed, requestToken:{}, result:{}", identify, result);
    return false;
}
 
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

本文链接: https://www.Java265.com/JavaProblem/202110/1501.html

最近发表

热门文章

好文推荐

Java265.com

https://www.java265.com

站长统计|粤ICP备14097017号-3

Powered By Java265.com信息维护小组

使用手机扫描二维码

关注我们看更多资讯

java爱好者