ReentrantLock详解

  • 时间:
  • 浏览:1
  • 来源:欢乐生肖APP下载_欢乐生肖APP官方

AQS使用1个 多多FIFO的等待歌曲队列表示排队等待歌曲锁的程序运行运行,队列底部形态如图:

队列头节点称作哨兵节点但会 哑节点,它不与任何程序运行运行关联,或多或少的节点与1个 多多等待歌曲程序运行运行关联。

具体来说是:遍历等待歌曲队列,找出第1个 多多等待歌曲被唤醒的节点Node,但会 将它插入到同步等待歌曲队列尾部,但会 就让等待歌曲被唤醒(具体的过程与1个 多多程序运行运行释放锁1个 多多或多或少程序运行运行获取所得过程相同)。

  1. Condition

Condition提供了1个 多多两种能力:程序运行运行获取锁1个 多多,但会 条件不满足,挂起程序运行运行释放资源;但会 条件满足,则唤醒程序运行运行继续处置。

next

  1. b) shouldParkAfterFailedAcquire

此办法是用来判断否有应该挂起当前节点对应的程序运行运行。允许挂起的条件是:prev节点的waitStatus=SIGNAL。或多或少情况报告的意思是:prev节点对应的程序运行运行在释放锁1个 多多应该唤醒(unpack)node节点对应的程序运行运行。

但会 公平锁、非公平锁的大累积逻辑相同,或多或少这里主要以非公平锁的源码来讨论。

Thread

表示或多或少Node在条件队列中,但会 等待歌曲某个条件而被阻塞

说明:先区分1个 多多概念,本节中同步等待歌曲队列指的是指的是AQS中的FIFO的队列。条件等待歌曲队列指的是ConditionObject对象上的等待歌曲队列。

  1. 2) tryAcquireNanos
    public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }

  1. 4) unparkSuccessor

此办法主要逻辑是:

假设初始情况报告,如下图所示:





以下1个 多多办法时获取超时放弃锁的源码,其中主要逻辑在doAcquireNanos中,和前面讨论的“获取锁”累积的(见acquireQueued办法)主要区别在于:在循环中会检测等待歌曲时间否有但会 超过指定时间,但会 超过了,没有将Node的waitStatus改为CANCELLED情况报告。

关于程序运行运行、synchronized的更多内容,还会 参考《程序运行运行-基础知识》,地址为:https://yq.aliyun.com/articles/414908

创建ConditionObject对象,1个 多多ConditionObject对象就让1个 多多普通的对象,没有哪此很糙的地方,就让提供了await、signal等办法,或多或少累积将在后面 完全分析。

-2

取回情况报告,同类但会 等待歌曲锁超时而取回的程序运行运行;地处或多或少情况报告的Node会被踢出队列,被GC回收

注意:新Node入队列就有检查并删除被CANCELLED的节点;真是Node1等待歌曲歌曲时间到后被唤醒后,在将被委托人情况报告改为CANCELLED时,但会 发现被委托人是最后1个 多多节点也会将Node1删除。

Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that rely on first-in-first-out (FIFO) wait queues. 



表示或多或少Node的继任Node被阻塞了,到时只能通知它

  1. 5) addWaiter

addWaiter是属于AbstractQueuedSynchronizer中的办法。此办法主要逻辑是:创建1个 多多Node,与当前程序运行运行关联,但会 尝试加入到FIFO队列的尾部。加入到FIFO队列时,但会 发现队列没有初始化过,即Head为空,没有先创建1个 多多空的不与任何程序运行运行相关联的Node最为Head;但会 将Node加入到队列尾部。

  1. 3) addConditionWaiter

此办法的主要逻辑如下:

Thread0执行完毕后,释放程序运行运行,将唤醒Head后面 第1个 多多情况报告为等待歌曲SIGNAL的节点对应的程序运行运行。

指向队列中前1个 多多节点

  1. ReentrantLock与synchronized对比
  1. 1 Synchronized

Synchronized是通过同步互斥来实现程序运行运行安全的;即同一时间只能1个 多多程序运行运行访问synchronized修饰的代码块或办法。其底部形态与功能如下:

属性名

Node

此办法的主要逻辑如下:

亲戚朋友 还会 看得人,ReentrantLock默认构造器是非公平的。

释放锁,主要逻辑是:



描述



当且仅当以下两中情况报告,此办法返回true。

  1. 4) isOnSyncQueue

此办法的主要逻辑如下:

  1. 3) tryRelease

此办法主要逻辑是:

公平锁和非公平锁的释放锁的过程是相同的,释放锁的流程图如下。

1

exclusiveOwnerThread代表持有所得程序运行运行信息,或多或少一旦上锁成功,只能将exclusiveOwnerThread设置为当前程序运行运行。

AbstractQueuedSynchronizer继承了AbstractOwnableSynchronizer,或多或少类只1个 多多变量:exclusiveOwnerThread,表示当前占用该锁的程序运行运行,但会 提供了相应的get,set办法。AQS使用此字段记录当前占有锁的程序运行运行。

如下代码示例中,还会 看得人公平锁和非公平锁的第1个 多多差别:非公平锁lock的第一步就让尝试通过compareAndSetState(0, 1)获取锁,但会 他何必 求公平,或多或少他上来就争抢锁。

  1. 2) await

释放锁,挂起当前程序运行运行,等待歌曲或多或少程序运行运行发起唤醒信号(signal);被唤醒1个 多多重新获取锁。此办法还会 认为和Object的wait等价。

-3





非公平锁上锁主要流程如下所示:

ReentrantLock还会 支持多条件等待歌曲,真是现原理如下:每次调用newCondition()办法,就有创建1个 多多ConditionObject对象,每个ConditionObject对象都还会 挂1个 多多等待歌曲队列;但会 希望一起等待歌曲多个条件,只只能简单的多次调用newCondition创建多个条件对象就好了。

此办法主要逻辑如下:

事实上concurrent包内或多或少类就有基于AQS构建,同类ReentrantLock,Semaphore,CountDownLatch,ReentrantReadWriteLock,FutureTask等。AQS处置了在实现同步容器时设计的多量细节难题。

AQS中1个 多多表示情况报告的字段state,ReentrantLock用它表示程序运行运行重入锁的次数,Semaphore用它表示剩余的许可数量,FutureTask用它表示任务的情况报告。对state变量值的更新都采用CAS操作保证更新操作的原子性。

Node的持有程序运行运行

ReentrantLock是两种非常常见的临界区处置手段,通过在执行代码前上锁保证同一时间只1个 多多程序运行运行能执行指定的代码块。ReentrantLock的底部形态与功能如下:

情况报告值

在AQS累积亲戚朋友 提到ReentrantLock中state记录的锁次数(当然也包括重入次数)。state=0代表当前没有程序运行运行持有锁。state>0代表加锁次数,第一次lock成功state=1,重入一次state++。

非公平锁上锁主要流程如下所示:

CONDITION

SIGNAL

AbstractQueuedSynchronizer简称AQS,是1个 多多用于构建锁和相关同步器的框架,它依赖于FIFO的等待歌曲队列实现。见AbstractQueuedSynchronizer的描述:

thread



类型

  1. 2 使用示例伪代码
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public void conditionWait() throws InterruptedException {
        // 但会

还会

获取锁,没有直接往后执行,但会

等待歌曲直到获取锁
        lock.lock();
        try {
            //资源否有但会

都准备好了?
            boolean resourceReady = false;
            if( !resourceReady ){
                //await会让当前程序运行运行释放其持有的锁并挂起
                condition.await();
            }
            // TODO 执行业务逻辑
        } finally {
            lock.unlock();
        }
    }

    public void conditionSignal() throws InterruptedException {
        // 但会

还会

获取锁,没有直接往后执行,但会

等待歌曲直到获取锁
        lock.lock();
        try {
            //资源否有但会

都准备好了?
            boolean resourceReady = true;
            if( resourceReady ){
                condition.signal();
            }
        } finally {
            lock.unlock();
        }
    }

但会 prev节点waitStatus<=0(当然不包括SIGNAL情况报告),没有通过CAS操作设置waitStatus= SIGNAL

Thread3申请锁时,但会 无法获取锁,或多或少创建1个 多多Node,但会 加入到等待歌曲队列中;如下图所示:

Node

compareAndSetState(0, 1)办法是基于CAS操作,尝试将state从0改为1。这是基于硬件指令集的原子操作,当且仅当state=0时将其修改为1。

nextWaiter

PROPAGATE 

如流程图中所示,主要流程基本和非公平锁一致,1个 多多多差别:

或多或少

等待歌曲情况报告

Node

注意,但会 程序运行运行获取锁失败,没有会在parkAndCheckInterrupt()办法中被挂起。1个 多多程序运行运行彻底释放锁资源1个 多多,会唤醒head后的1个 多多节点对应的程序运行运行,当某1个 多多程序运行运行被唤醒1个 多多,继续执行parkAndCheckInterrupt()办法中LockSupport#park1个 多多的代码。

说明:代码的中是从tail向head遍历查找waitStatus=SIGNAL的节点,但结果与这里说的是一样的,即最终也是找Head后的第1个 多多waitStatus=SIGNAL的节点。见unparkSuccessor办法

AQS中维护了1个 多多FIFO队列,用于记录等待歌曲的程序运行运行,上锁和释放锁的过程还会 认为是程序运行运行入队列和出队列的过程;获取只能锁,入队列等待歌曲;出队列时被唤醒。

情况报告

或多或少累积简要的介绍加锁、释放锁的流程,让亲戚朋友 对ReentrantLock1个 多多整体的概念,后面 将通过源码完全分析实现细节。

ReentrantLock本质上是通过1个 多多队列来完成同步的。但会 每个Node与1个 多多程序运行运行关联,只只能做好对队列节点的同步处置,既还会 完成多程序运行运行的同步处置。

描述



接下来亲戚朋友 以1个 多多简单的示例描述ReentrantLock的等待歌曲锁、释放锁的原理。为了方便描述,以下示例中节点和程序运行运行一一对应,同类Node1与Thread1对应,依次类推。

waitStatus

下1个 多多等待歌曲condition的Node

  1. c) FairSync#tryAcquire
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
           // 但会

head的next节点对应的程序运行运行就有当前程序运行运行,没有当前程序运行运行只能尝试获取锁,1个

多多还会

保证按照获取所得顺序公平的获取锁。
                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;
        }

这里仅给出流程图,后面 会通过代码完全讲述或多或少流程。

  1. 4 释放锁
  1. 1) unlock
    public void unlock() {
        sync.release(1);
    }

关于synchronized的原理,还会 参考《互斥同步-锁》,地址为:https://yq.aliyun.com/articles/414939



  1. 4) tryAcquire

此办法是尝试获取锁。此时但会 state=0,说明所但会 被释放了,或多或少还会 重新尝试上锁操作,即进行compareAndSetState(0, 1)操作;但会 state!=0,但会 当前程序运行运行持有锁,没有重入,更新state,即state++。

int 

假设3s中到了1个 多多,Thread1被唤醒(挂起的1个 多多指定了时间,或多或少等待歌曲时间到就有被唤醒),此就有将被委托人的情况报告改成CANCELLED,表示等待歌曲被从队列中移除;如下图所示:

使用在共享模式头Node有但会 地处或多或少情况报告, 表示锁的下一次获取还会 无条件传播

addWaiter是属于AbstractQueuedSynchronizer中的办法。此办法主要逻辑为:

acquire属于AbstractQueuedSynchronizer中的办法;此办法主要逻辑如下:

CANCELLED 

  1. 5) signal

简单来说是:唤醒程序运行运行。

Java中的CAS操作基本上就有通过unsafe实现的,代码如下:

但会 FIFO队列尚未初始化,没有先初始化;但会 但会 初始化了,没有尝试将node加入到队尾,失败则再试一次。

-1

以下是ReentrantLock的1个 多多构造器,如下所示:

但会 prev节点waitStatus>0,即为CANCELLED情况报告时,只能将prev从队列中移除。重试此操作直到找到1个 多多prve节点的情况报告不为CANCELLED。

每次1个 多多程序运行运行释放锁1个 多多,从Head现在开始英文了了向后寻找,找到1个 多多waitStatus是SIGNAL的节点,但会 通过LockSupport.unpark唤醒被挂起的程序运行运行,被挂起的程序运行运行继续执行尝试上锁逻辑。新的程序运行运行尝试获取锁时,但会 获取只能锁,但会 创建1个 多多Node并加入到队尾,但会 将被委托人挂起,等待歌曲挂起时间到但会 等待歌曲被prev对应的节点唤醒。

prev

相比于synchronized关键字,ReentrantLock等多的同类于亲戚朋友 使用zookeeper实现的等待歌曲队列。Zookeeper的队列示例还会 参考博客《Zookeeper使用案例》地址为:https://yq.aliyun.com/articles/272103

初始情况报告

但会 此时有新程序运行运行申请锁,没有在入队列过程中会顺便将地处CANCLE情况报告的节点移除。如下图所示:

指向队列中后1个 多多节点