• AQS之ReentrantLock分析 (四)


    1.AQS 子类

    在这里插入图片描述

    1. Semphore: 共享锁案例
    2. ReentrantLock: 排他锁案例
    3. ReentrantReadWriteLock: 共享锁和排它锁案例
    4. ThreadPoolExecutor
    5. CountDownLatch: 共享锁案例

    在这里插入图片描述

    2.ReentrantLock 简介

    在这里插入图片描述

    ReentrantLock 为可重入锁

    2.1 Sync

    和Semaphore相似,ReentrantLock也是通过一个抽象类Sync继承了AbstractQueuedSynchronizer,Sync又有两个子类FairSync和UnFairSync,分别代表公平锁和非公平锁。

    在这里插入图片描述

    ReentrantLock有两个构造方法,public ReentrantLock(boolean fair)指定公平策略,public ReentrantLock()无参构造器则默认使用非公平锁。

    在这里插入图片描述

    2.2 state

    state在不同的AQS里有不同的含义,比如Semaphorepermits数量和CountDownLatch的计数值count,在ReentrantLock里可以理解为锁的重入次数。

    在ReentrantLock里,state固定初始化为0,代表无锁状态,线程获得锁计数修改为1,后续该线程每次重入都会使计数加1,离开锁时减1,直到state=0重新加入无锁状态,其他线程可以竞争获得锁。

    AbstractQueuedSynchronizer.state

    在这里插入图片描述

    2.3 exclusiveOwnerThread

    exclusiveOwnerThread字段是由AbstractQueuedSynchronizer的父类AbstractOwnableSynchronizer提供的一个字段,可以用于代表当前获取资源的线程。

    在这里插入图片描述

    在这里插入图片描述

    3.获取锁

        public static void main(String[] args){
            ReentrantLock lock = new ReentrantLock();
    
    //        lock.lock();
    //
    //        lock.unlock();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    • FairSync: 公平锁
    • NofairSync: 非公平锁
    3.1 非公平锁 NofairSync
            final void lock() {
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    acquire(1);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    • tryAcquire(): 尝试获取资源
    • addWaiter(): 添加Node到等待队列中, 将没有获取到锁资源的线程甩到队列的尾部。
    • acquireQueued(): 将已经在队列中的node尝试去获取锁否则挂起。
            protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
    
     boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        //无锁状态,CAS获取锁
        if (c == 0) {
            if (compareAndSetState(0, acquires)) {
                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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    (1)获取当前state,根据是否有锁状态分别处理
    (2)无锁状态:CAS修改state拿锁,修改成功则说明获取锁成功,否则说明有其他线程竞争到了锁,反悔失败
    (3)有锁状态:判断持锁线程是否是当前线程,是则进行重入,state加1,否则返回失败

        private Node addWaiter(Node mode) {
            Node node = new Node(Thread.currentThread(), mode);
            // Try the fast path of enq; backup to full enq on failure
            Node pred = tail;
            if (pred != null) {
                node.prev = pred;
                if (compareAndSetTail(pred, node)) {
                    pred.next = node;
                    return node;
                }
            }
            enq(node);
            return node;
        }
    
        private Node enq(final Node node) {
            for (;;) {
                Node t = tail;
                if (t == null) { // Must initialize
                    if (compareAndSetHead(new Node()))
                        tail = head;
                } else {
                    node.prev = t;
                    if (compareAndSetTail(t, node)) {
                        t.next = node;
                        return t;
                    }
                }
            }
        }
    
    • 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

    如果没有获得资源的话, 会添加到一个CLH队列中

    3.2 公平锁 FairSync

    在这里插入图片描述

    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
        	//没有前驱节点才能通过CAS拿锁
            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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    公平锁的特性就是先到先得,因此和非公平锁的差异就是无锁状态下只有队列里没有当前线程的前驱节点时才能去竞争锁。

    4.释放锁

    在这里插入图片描述
    在这里插入图片描述

    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    只需要对state进入减操作,当state=0了,说明锁完全释放,这时候才返回成功并且将setExclusiveOwnerThread置为null,说明无线程持有锁

        private void unparkSuccessor(Node node) {
            /*
             * If status is negative (i.e., possibly needing signal) try
             * to clear in anticipation of signalling.  It is OK if this
             * fails or if status is changed by waiting thread.
             */
            int ws = node.waitStatus;
            if (ws < 0)
                compareAndSetWaitStatus(node, ws, 0);
    
            Node s = node.next;
            if (s == null || s.waitStatus > 0) {
                s = null;
                for (Node t = tail; t != null && t != node; t = t.prev)
                    if (t.waitStatus <= 0)
                        s = t;
            }
            if (s != null)
                LockSupport.unpark(s.thread);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    通过判断Node的waitStatus进行释放锁的操作, 如果waitStatus<0的话设置为等待节点

    在这里插入图片描述

  • 相关阅读:
    小程序云开发笔记二
    【21-40】计算机网络基础知识(非常详细)从零基础入门到精通,看完这一篇就够了
    IDEA创建Springboot多模块项目
    关于生产企业原材料的订购与运输问题
    系统架构设计高级技能 · 安全架构设计理论与实践
    GoogleTest学习笔记(一)
    Ant Design Pro从零到一(认识AntD)
    领域内容第18名
    Java之BigDecima容器学习心得
    WebMagic抓取医院科室,医生信息实战及踩坑
  • 原文地址:https://blog.csdn.net/qq_43141726/article/details/127722806