做一个分布式锁的思路思考(个人思考,不具备权威性,有错误的话希望能得到指出)

分布式锁必须的条件

  1. 独占性
  2. 高可用
  3. 防死锁
  4. 不乱抢
  5. 可重入

独占性

  1. 概念:锁的独占性又称为排他性,就是说一个资源被一个线程所占用,那么其他想访问本资源的线程都需要等待。

  2. 为什么需要:因为一个资源同时被多个线程操作就会出现重复写,导致数据成为脏数据不可用。例如AB线程同时需要将一个资源减一,正常情况总资源减二,但是若是A执行慢了,B在A执行的时候也要对资源操作,AB 拿到的总数都是一样的,各自去对总数操作减一,就会导致明明消耗了两个资源,记录却只有一个。

  3. 实现依据:
    2.1 操作系统:互斥量(lock/unlock)、信号量(维护一个计数器,资源来了+1,请求来了-1,大于0说明有资源,小于0等待)
    2.2 java:
    2.2.1 synchronized:每个Java对象都关联着一个监视器(Monitor),当线程想要执行synchronized代码块或方法时,它必须先获得该对象的监视器。在字节码层面,synchronized通过monitorentermonitorexit这两条指令来实现锁的获取和释放。monitorenter: 当线程进入synchronized代码块或方法时,会在其字节码中插入monitorenter指令。这会导致线程尝试获取对象的监视器锁。如果锁未被其他线程持有,则该线程获得锁并执行同步代码。如果锁已被持有,则该线程将阻塞,直到锁被释放。
    monitorexit: 在同步代码块或方法结束(包括正常结束和因异常退出)时,会插入monitorexit指令来释放锁。确保锁总是能被正确释放,即使发生异常。
    这个也就是相当于是字节码层次上添加互斥量
    2.2.2 ReentrantLock : 基于AbstractQueuedSynchronizer(AQS)框架实现的。AQS使用了一个内部的FIFO等待队列来管理线程,当线程尝试获取锁失败时,会被封装成Node节点加入队列,然后阻塞等待。锁的释放会唤醒队列中的下一个等待线程。ReentrantLock支持公平和非公平模式,通过内部的公平性策略决定线程获取锁的顺序。
    在这里插入图片描述
    公平锁(FairSync):

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();// 获取当前线程
            int c = getState(); // 得到当前锁的层数
            if (c == 0) { // 可加锁
                if (!hasQueuedPredecessors() && // 前面没有排队线程
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current); // 抢占成功
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) { // 已经获得过了
                int nextc = c + acquires;// 再加一层锁,不重新获取锁
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

非公平锁(NonfairSync):

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) { // 少了 hasQueuedPredecessors
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

2.3 redis、zookeeper、mysql: 这三个在实现独占性也是靠的互斥量的思路。

高可用

  1. 概念:不能因为某一个部分坏了,导致所有的项目服务都不能使用
  2. 为什么需要: 因为服务每多宕机一秒钟,损失就会指数级爆炸。
  3. 实现依据:主从备份,集群部署。
    主从备份:master/slaver 的模式,可以保证即使一台机器坏了数据依然有备份还能用。
    集群部署:多个主从备份+哨兵,可以将影响降到最小

防死锁

  1. 概念: 多个线程互相等待对方释放资源而永久地阻塞,导致它们都无法继续执行下去。简单来所就是AB两个线程工作需要同时拥有CD两个资源才能进行,但是现在A抢了C,B抢了D互相不释放自己的,导致两个线程一直处于等待状态。
  2. 为什么需要: 死锁多了,等待的线程就多了,内存就爆了,服务就挂了,你就被开了。
  3. 实现依据:从死锁产生的四个条件入手(互斥条件、请求与保持条件、不剥夺条件、循环等待条件),破坏形成条件即可破环死锁。
    3.1 互斥条件:资源不共享,一个资源不能同时被多个线程操作。这个不好破坏哦,不能把一百块钱撕开一人当50去用啊。
    3.2 请求与保持条件:线程工作是需要多个资源的,但是没得到所有的资源就不能干活,并且已经拿到手的资源也不会释放。就是你上班需要工资和电脑敲代码,但是公司一直不给你电脑,你不会说要把工资还回去吧,只能是一直等着发电脑干活。这个怎么破坏呢?一种是撤销,公司刚给你招进来就发现没钱买电脑了,直接撤回了一个offer,给你说拜拜~,一种是循环检测,公司人事天天巡逻,发现一个拿工资不干活的,就给你开了。还有一种就是给你补齐资源,让你把活干完,一般不会采取这个。
    3.3 不剥夺条件: 进程已获得的资源在未使用完毕之前,不能被其他进程强行剥夺。就是说你得到的这个工作岗位,你不走之前别人也不能再获得。怎么破环呢?懂得都懂,直接给你开了就好了。
    3.4 循环等待条件: 存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被链中下一个进程所请求。就是你有工资没电脑,别人有电脑没工资,两个人都犟得很,一个不给你电脑就不工作,一个人不给工资就不还电脑。两个人都在无限期的等待。

不乱抢

  1. 概念: 自己加的锁只能自己去释放。
  2. 为什么需要:大家都能解别人锁了,还加什么锁。
  3. 实现依据:每把锁都需要一个唯一标识,解锁之前先看看是不是自己锁。

可重入

  1. 概念:每个资源可能不止操作一次,那么我拿到这个锁以后,再次操作是不需要再重新获取一把新的锁。
  2. 为什么需要: 因为新建一把锁开销很大,并且若是操作次数多,还需要按顺序解锁很麻烦。
  3. 实现依据:给锁增加层数,用一次增加一层,那么解锁的时候就是释放一层,释放完了就删掉。
  • 32
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值