• AQS之共享锁分析


    1.AQS 内部体系架构

    在这里插入图片描述

    • FairSync: 公平锁
    • NoFairSync: 非公平锁

    在这里插入图片描述

    • Shared: 共享模式
    • Exclusive: 排他模式

    2.获取资源

    在这里插入图片描述

    尝试获取共享资源, 返回值为整数, 负数为失败, 0为成功, 但是其他线程无法再成功, 正数为成功, 其他线程也可以成功。
    由于共享锁允许多个线程同时获取成功,因此可以用 返回值代表还能有几个线程可以继续获取资源,但并不是强制性的。

    2.1 尝试失败后的处理 doAcquireShared()
    private void doAcquireShared(int arg) {
    	//加到队列里,和排斥锁一样
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                    	//成功,处理队列中的节点
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        //和排斥锁一样,不响应中断
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                //和排斥锁一样
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
        	//和排斥锁一样
            if (failed)
                cancelAcquire(node);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    获取资源成功后,需要执行setHeadAndPropagate(node, r);

    2.2 处理队列中的节点 setHeadAndPropagate()
    private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
         
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
           	//唤醒队列的节点
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 设置新的头结点,把获取到资源的当前节点设置为头结点,即头结点是最后一个获取到资源的线程节点
    2. 唤醒节点
      (1)三个条件满足其一
      a. 尝试获取资源的返回值propagate 大于0,还有其他线程有希望获取到资源
      b. 原头结点或新头结点是null或者状态小于0,说明有其他操作进行了修改
      c.状态小于0说明是SIGNAL或者PROPAGATE,即使PROPAGATE也有可能会在其他的操作中变成了SIGNAL,因此需要进行唤醒。
      (2)下一个节点为共享节点或者null

    3.释放资源

    在这里插入图片描述

    3.1 尝试成功后的处理 doReleaseShared()
    private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                //signal需要唤醒阻塞线程
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    对头节点的处理。

    if (h == head) 的判断则说明如果处理过程中头结点发生变化则需要重试。

    4.其他获取资源

    4.1 响应中断 acquireSharedInterruptibly()

    在这里插入图片描述

        private void doAcquireSharedInterruptibly(int arg)
            throws InterruptedException {
            final Node node = addWaiter(Node.SHARED);
            boolean failed = true;
            try {
                for (;;) {
                    final Node p = node.predecessor();
                    if (p == head) {
                        int r = tryAcquireShared(arg);
                        if (r >= 0) {
                            setHeadAndPropagate(node, r);
                            p.next = null; // help GC
                            failed = false;
                            return;
                        }
                    }
                    if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                        throw new InterruptedException();
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在尝试获取资源和阻塞结束后检测到中断后抛出异常。

    4.2 超时 doAcquireSharedNanos()
    private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
                throws InterruptedException {
        //检测超时时间
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return true;
                    }
                }
                nanosTimeout = deadline - System.nanoTime();
                //检测超时时间
                if (nanosTimeout <= 0L)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                	//如果时间太短了,直接自旋而不是阻塞,提高性能
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    1. 排队获取资源时先判断是否已经超时
    2. 每次判断是否需要阻塞时都先判断是否超时
    3. 阻塞时增加检查,时间不足只进行自旋,减少阻塞的性能消耗
    4. 阻塞时阻塞指定时间
  • 相关阅读:
    Netty心跳检测
    元宇宙基础知识全汇总
    MySQL中常见的日志文件
    cl 和 “clangtidy“分别是什么?是同一样东西吗?
    拿铁DHT-PHEV座舱智能程度体验笔记(超详细)
    P2458 [SDOI2006]保安站岗
    高德地图爬虫实践:Java多线程并发处理策略
    Pretrain-finetune、Prompting、Instruct-tuning训练方法的区别
    CMMI V2.2模型介绍
    python --opencv图像处理(图像腐蚀与图像膨胀)
  • 原文地址:https://blog.csdn.net/qq_43141726/article/details/127704043