• ReentrantLock可重入、可打断、Condition原理剖析


    本文紧接上文的AQS源码,如果对于ReentrantLock没有基础可以先阅读我的上一篇文章学习ReentrantLock的源码

    ReentrantLock锁重入原理

    重入加锁其实就是将AQS的state进行加一操作

    然后释放锁资源将AQS的state进行减一操作

    当state为0时才会彻底的释放锁资源

    ReentrantLock可打断原理

    在ReentrantLock中可打断就是在等待锁的过程中可以被interrupt打断(需要调用lockInterruptibly),lock方法设置了打断标记,但是只有在线程获得锁的时候才能知道自己有没有在阻塞的过程中有没有被打断。

    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())// 我们这边会检查打断,如果打断的话返Thread.interrupted()
                    interrupted = true;  // 这里将打断标记置为true后,继续进入循环。直到获得到锁返回标记
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    

    首先我们需要调用加锁的lockInterruptibly()方法

    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
    

    可打断主要原因在如下代码解释。用异常代替了返回标记,让线程可以直接再park的过程中直接结束

    private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();  // 再被打断的时候不会将其标记置为true,而是直接抛出一个异常,打断当前的等待。
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    

    ReentrantLock条件变量原理

    Condition是一个接口,实际上是ReentrantLock的ConditionObject类作为其实现类。

    首先我们创建一个condition就会调用其构造方法。其实就是产生一个新的conditionObject

    final ConditionObject newCondition() {
        return new ConditionObject();
    }
    

    await()源码

    接下来,简单剖析一个源码

    • 首先我们得知道,ConditionObject中维护着一个等待的双向链表,其实和阻塞链表是很相似的,不同在于不需要前驱进行唤醒。然后在ConditionObject中维护头和尾的引用就是firstWaiter和lastWaiter成员变量。
    public final void await() throws InterruptedException {
        if (Thread.interrupted())   // 如果被打断,抛异常
            throw new InterruptedException();
        Node node = addConditionWaiter();  // 1.向AQS中添加一个等待链表的node
        int savedState = fullyRelease(node);
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) { // 是否在阻塞链表中
            LockSupport.park(this); // 不是的话直接进行阻塞起来
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)// 被打断了就得把它放阻塞链表
                break;
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)  // 多线程的遗留状态处理
            interruptMode = REINTERRUPT;   
        if (node.nextWaiter != null) // clean up if cancelled
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }
    
    1. 添加等待node
    private Node addConditionWaiter() {
        Node t = lastWaiter;
        // If lastWaiter is cancelled, clean out.
        if (t != null && t.waitStatus != Node.CONDITION) {  
            unlinkCancelledWaiters(); // 这个和阻塞队列相似不过是全部遍历,清除不在等待队列上的node
            t = lastWaiter;
        }
        Node node = new Node(Thread.currentThread(), Node.CONDITION);
        if (t == null)              // 添加到waitting尾部 
            firstWaiter = node;
        else
            t.nextWaiter = node;
        lastWaiter = node;
        return node;
    }
    

    signal源码

    signal唤醒源码,简单介绍就是直接将等待的firstWaiter指向的等待链表的第一个进行解除阻塞,然后将其放入阻塞链表中。不过多赘述。

    public final void signal() {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        Node first = firstWaiter;
        if (first != null)
            doSignal(first);
    }
    

    __EOF__

  • 本文作者: captain
  • 本文链接: https://www.cnblogs.com/duizhangz/p/16267995.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    电场刺激响应性和AIE水凝胶/调控发射波长及亮度AIE微球/AIE糖肽聚合物的研究
    十三、【VUE-CLI】$nextTick(待办事项案例 · 第六版)
    软件工程总结
    数据湖(十四):Spark与Iceberg整合查询操作
    PAT (甲级) 2022年夏季考试 c++ 满分题解
    文本摘要实战:基于seq2seq+attention实现文本摘要
    puppeteer只生成一页pdf的问题
    如何用 Prometheus Operator 监控 K8s 集群外服务?
    maven compile无效的标记: --release
    【目标检测】SAHI: 切片辅助推理和微调小目标检测
  • 原文地址:https://www.cnblogs.com/duizhangz/p/16267995.html