分布式锁

一、基于redis实现
可用于实现接口幂等性,防止重复下单等。

package lock;

/**
 * Created with IDEA
 * author:liuhaotian
 * Date:2019/9/14 15:51
 * Description: 分布式锁
 */
public class DistributedLock {
    /**
     * 基于redis实现分布式锁
     * 基于 REDIS 的 SETNX()、GET()、GETSET()方法做分布式锁
     * 这个方案的背景主要是在 setnx() 和 expire() 的方案上针对可能存在的死锁问题(setnx() 执行成功了,expire失败了),
     * 做了一些优化。
     */
    /**
     * 使用步骤
     * 1.setnx(lockkey, 当前时间+过期超时时间),如果返回 1,则获取锁成功;如果返回 0 则没有获取到锁,转向 2。
     * 2.get(lockkey) 获取值 oldExpireTime ,并将这个 value 值与当前的系统时间进行比较,如果小于当前系统时间,
     * 则认为这个锁已经超时,可以允许别的请求重新获取,转向 3。
     * 3.计算 newExpireTime = 当前时间+过期超时时间,然后 getset(lockkey, newExpireTime)
     * 会返回当前 lockkey 的值currentExpireTime。
     * 4.判断 currentExpireTime 与 oldExpireTime 是否相等,如果相等,说明当前 getset 设置成功,获取到了锁。
     * 如果不相等,说明这个锁又被别的请求获取走了,那么当前请求可以直接返回失败,或者继续重试。
     * 5.在获取到锁之后,当前线程可以开始自己的业务处理,当处理完毕后,比较自己的处理时间和对于锁设置的超时时间,
     * 如果小于锁设置的超时时间,则直接执行 delete 释放锁;如果大于锁设置的超时时间,则不需要再锁进行处理
     */
    private static final int DEFAULT_EXPIRE_TIME = 60;

    public boolean lock(String key) {
        //1.0
        long keyValue = System.currentTimeMillis() + DEFAULT_EXPIRE_TIME;
        if (1 == RedisUtil.setNx(key, String.valueOf(keyValue))) {
            return true;
        }
        //2.0
        long oldExpireTime = Long.parseLong(RedisUtil.get(key));
        //如果时间没过期,则说明锁还有效,否则 说明锁已失效
        if (oldExpireTime < System.currentTimeMillis()) {
            //旧锁过期,生成新的过期时间
            long newExpireTime = System.currentTimeMillis() + DEFAULT_EXPIRE_TIME;
            //设置新的过期时间并判断,此时旧的过期时间是否为前边判断的,如果不是,则说明已经有其他线程设置了新的过期时间
            //也就是其他线程抢占了该锁。
            long curExpireTime = Long.parseLong(RedisUtil.getset(key, String.valueOf(newExpireTime)));
            if (curExpireTime == oldExpireTime) {
                return true;
            }
        }
        return false;
    }
}

package lock;

/**
 * Created with IDEA
 * author:liuhaotian
 * Date:2019/9/14 15:58
 * Description: 模拟redis操作
 */
public class RedisUtil {

    public static int setNx(String key,String value){
        return 1;
    }

    public static String get(String key){
        return "";
    }

    public static String getset(String key,String newValue){
        return "";
    }

    public static void expire(String key,int time){

    }

}

二、基于zookeeper
在这里插入图片描述
在这里插入图片描述
临时节点:跟zookeeper的连接断开后,临时节点会自动被删掉。
在这里插入图片描述
1.订阅一个节点的变更通知
2.重写节点变更时回调的方法(watch机制)

Zookeeper典型应用场景:
1.数据发布订阅-配置中心
2.master选举
3.集群管理
4.分布式锁

在这里插入图片描述
利用临时节点实现分布式锁:
0. implement Lock接口
1.先尝试创建一个key的临时节点,成功则获取锁成功
2.否则获取锁失败,阻塞继续获取锁
3.用CountDownLatch 计数器实现阻塞
4.订阅通知:当已经获取锁的线程释放锁之后,会发布一个通知,当前线程收到通知后,countDown 唤醒该线程继续抢占锁,并取消订阅。
5.如果再次抢占锁失败,则继续阻塞重复以上操作。
//cd1.countDown() 之后 如果计数器减到0,则该线程唤醒。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
用zookeeper实现分布式锁的缺点:
每次都会有大量节点去抢占锁,不适合大型集群系统。
在这里插入图片描述
利用临时顺序节点实现分布式锁:*
在这里插入图片描述

  1. 在节点key下面创建一个临时顺序节点
  2. 获取子节点列表,判断自己是否为最小的子节点,如果是,则表示获取锁成功,否则:只对 比自己小一号的节点注册,然后阻塞等待。
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值