AQS的许可证颁发、利用AQS实现独占锁

XiaoYeGe

共 10905字,需浏览 22分钟

 ·

2023-05-27 20:25

11-6 AQS的许可证颁发

AQS在Semaphore的应用

Semaphore中,state表示 许可证 剩余数量

11-6-1 Semaphore 源码

主要方法

Acquire : 获取许可证

Release : 释放许可证

acquire

      
        public void acquire(int permits) throws InterruptedException {
      
      
            if (permits < 0) throw new IllegalArgumentException();
      
      
            sync.acquireSharedInterruptibly(permits);
      
      
        }
      
    

acquireSharedInterruptibly

      
        
          public final void acquireSharedInterruptibly(int arg)
        
      
      
                throws InterruptedException {
      
      
            if (Thread.interrupted())
      
      
                throw new InterruptedException();
      
      
            if (tryAcquireShared(arg) < 0)
      
      
                doAcquireSharedInterruptibly(arg);
      
      
        }
      
    

tryAcquireShared 有两种实现 , 公平和不公平

NonfairSync extends Sync 非公平情况

      
        protected int tryAcquireShared(int acquires) {
      
      
            return nonfairTryAcquireShared(acquires);
      
      
        }
      
      
        final int nonfairTryAcquireShared(int acquires) {
      
      
            for (;;) {
      
      
                int available = getState();
      
      
                int remaining = available - acquires;
      
      
                if (remaining < 0 || compareAndSetState(available, remaining))
      
      
                    return remaining;
      
      
            }
      
      
        }
      
    

主要作用 :  获取许可证, 其返回值

正数 : 代表获取成功, 负数 : 获取失败

首先获取当前可用许可证的数量 int available = getState();

其次计算当前可用许可证数量是否满足本次要求 , acquires 想要获取许可证的数量

只要当前可用许可证数量 -  想要获得的许可证数量 <  0, 就会返回负数;

上一个调用该方法的方法就会将当前线程放入等待队列中等待

11-6-2 AQS在Semaphore的应用

Semaphore中, state表示许可证的剩余数量

tryAcquire () 方法,判断 nonfairTryAcquireShared大于等于0的话,代表成功

这里会先检查剩余许可证数量够不够这次需要的,用减法来计算,如果直接不够,那就返回负数,表示失败,如果够了,就用自旋   +   compareAndSetState 来改变 state状态,直到改变成功就返回正数;   或者是期间如果被其他人修改了 导致剩余数量不够了,那也返回负数代表获取失败

11-7 利用AQS实现独占锁

AQS在ReentrantLock的应用

分析释放锁的方法 tryRelease

11-7-1 ReentrantLock  | unlock

      
        public void unlock() {
      
      
            sync.release(1);
      
      
        }
      
    
      
        public final boolean release(int arg) {
      
      
            if (tryRelease(arg)) {
      
      
                Node h = head;
      
      
                if (h != null && h.waitStatus != 0)
      
      
                    unparkSuccessor(h);
      
      
                return true;
      
      
            }
      
      
            return false;
      
      
        }
      
    

第一步就调用了 tryRelease 方法

      
        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;
      
      
        }
      
    

判断当前线程是不是持有锁的线程

      
        Thread.currentThread() != getExclusiveOwnerThread()
      
    

只有持有锁的线程才能解锁 , 否则抛出异常

当前 state数量   -  需要释放的数量 =   0  的情况下才会去释放锁

      
        int c = getState() - releases;
      
      
            if (c == 0) {
      
      
                free = true;
      
      
                setExclusiveOwnerThread(null);
      
      
            }
      
    

tryRelease 方法返回 true以后, 调用该方法者就会执行如下代码

      
                Node h = head;
      
      
                if (h != null && h.waitStatus != 0)
      
      
                    unparkSuccessor(h);
      
      
                return true;
      
    

唤醒后续节点 unparkSuccessor

11-7-2 ReentrantLock  | lock

      
        public void lock() {
      
      
            sync.lock();
      
      
        }
      
      
        abstract void lock();
      
    

此处是一个抽象方法 , 没有被实现

因为根据公平 /非公平有两种锁的策略

4a7746ff56606cb46561618a36d6bc28.webp

以非公平为例

      
        final void lock() {
      
      
            if (compareAndSetState(0, 1))
      
      
                setExclusiveOwnerThread(Thread.currentThread());
      
      
            else
      
      
                acquire(1);
      
      
        }
      
    

首先进行 CAS操作

      
        if (compareAndSetState(0, 1))
      
    

期望值是 0, 意味着当前没有任何人持有这把锁的时候, state为0的时候才能加锁成功

并且将当前线程设置为持有锁的线程

      
        setExclusiveOwnerThread(Thread.currentThread())
      
    

另一个分支是 acquire

acquire

      
        public final void acquire(int arg) {
      
      
            if (!tryAcquire(arg) &&
      
      
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
      
      
                selfInterrupt();
      
      
        }
      
    

首先调用 tryAcquire

tryAcquire 实现在 ReentrantLock类中

非公平为例

      
        protected final boolean tryAcquire(int acquires) {
      
      
            return nonfairTryAcquire(acquires);
      
      
        }
      
    
      
        final boolean nonfairTryAcquire(int acquires) {
      
      
            final Thread current = Thread.currentThread();
      
      
            int c = getState();
      
      
            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;
      
      
        }
      
    

判断该锁是否被持有

      
        int c = getState()
      
    

c == 0 , 说明无人持有该锁, 通过CAS将该锁持有, 设置当前线程持有该锁

      
        if (compareAndSetState(0, acquires)) {
      
      
            setExclusiveOwnerThread(current);
      
      
            return true;
      
      
        }
      
    

c != 0 并且当前线程是该锁的持有者

      
        else if (current == getExclusiveOwnerThread()) {
      
      
            int nextc = c + acquires;
      
      
            if (nextc < 0) // overflow
      
      
                throw new Error("Maximum lock count exceeded");
      
      
            setState(nextc);
      
      
            return true;
      
      
        }
      
    

则说明本次获取锁 , 对于当前线程来说是一个重入操作, 所以stat = c + acquires

然后设置 stat值  setState(nextc);

c != 0 并且当前线程 不是 该锁的持有者

则代表该锁已经被其他线程持有了 , 则获取失败

11-7-3 AQS在ReentrantLock的应用

分析 释放锁 的方法 tryRelease ()

由于是可重入的,所以 state代表重入的次数 ,每次释放锁,先判断是不是当前持有锁的线程释放的,如果不是就抛异常,如果是的话,重入次数就减 1 ,如果减到了 0,就说明完全释放了,于是free就是true,并且把state设置为0。

加锁的方法


近期热文

参考资料

1 | JUC


责编  | 小耶哥

本期作者  | 小耶哥

平台建设及技术支持  | 小耶哥


浏览 46
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报