AQS小总结

前言:将AQS的知识点做一个总结. 梳理了独占共享两种模式下的加解锁流程,以及两种锁的异同点. 适合日后复习

目录

一、AbstractQueuedSynchronizer

1. 独占锁

加锁

解锁

2. 共享锁

加锁

解锁

3. 独占/共享模式对比

加锁成功

加锁失败

解锁

4. 重要属性

waitStatus

state

二、ReentrantLock

ReentrantLock类结构

ReentrantLock源码

三、Semaphore

Semaphore类结构

Semaphore源码

四、CountDownLatch

CountDownLatch类结构

CountDownLatch源码

ReentrantReadWriteLock



一、AbstractQueuedSynchronizer

该抽象类主要实现了:在获取锁失败时,线程如何阻塞,如何唤醒.。使用的逻辑结构为【同步等待队列】

如何获取锁,以及获取锁是成功还是失败,由子类去定义。AQS则提供了获取锁失败后,入队/出队、阻塞/唤醒的实现。

队列何时初始化?【尝试加锁】失败后,在addWaiter方法中,若tail为空,则新建一个Node作为头结点

何时入队?【尝试加锁】失败后,尾插到队尾

何时出队?当队列中有新的结点加锁成功时,原head指向的结点出队,随后head指向新的结点

1. 独占锁

加锁

尝试加锁,成功则方法结束;加锁失败则将此线程尾插到"同步等待"队列

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

protected boolean tryAcquire(int arg) :tryAcquire方法由其实现类实现:返回是否成功获取锁( 即是否成功将state由0变为1 ) 

private Node addWaiter(Node mode) :将当前线程封装为Node, 并将其插入到队尾. 没初始化队列会先初始化

final boolean acquireQueued(final Node node, int arg) 加锁成功则跳出方法,执行业务逻辑;或park此结点

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node)pred.ws == -1,则返回true,否则CAS设置pred.ws == -1。并把CANCELLED的结点移除队列

解锁

尝试解锁,解锁失败则直接返回false,成功则唤醒"同步等待"队列中第一个满足要求的结点,并返回true

    // 解锁
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0) // ≠0实际上就只能为-1
                unparkSuccessor(h); // 唤醒后继结点
            return true;
        }
        return false;
    }

private void unparkSuccessor(Node node) :node.waitStatus<0 且 有后继结点,则unpark其后继【LockSupport.unpark(node.next.thread)】;否则取最靠前的、waitStatus<=0的结点

2. 共享锁

加锁

尝试获取锁,返回负数则表示需要进行"阻塞"

    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

 

 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node)pred.ws == -1,则返回true,否则CAS设置pred.ws==-1。并把CANCELLED的结点移除队列.【同独占锁调用的相同】

private void setHeadAndPropagate(Node node, int propagate) :设置头节点并传播唤醒,也就是说,当一个结点加共享锁成功,它会执行unparkSuccessor(Node node),进而唤醒后继结点。具体实现主要在doReleaseShared()方法👇

    private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
        
        // propagate表示剩余的资源. 
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }


    /* 
        简而言之:不断执行unparkSuccessor(h)操作,来唤醒后继结点,
        退出条件为h == head,表示如果没有新的结点来代替现有头结点则退出
        unparkSuccessor方法同独占锁
    */
    private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

解锁

尝试解锁,失败返回false;成功则执行doReleaseShared(),该方法会唤醒后继结点

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

3. 独占/共享模式对比

加锁成功

共同点:尝试加锁,失败则addWaiter并自旋(加锁->阻塞)

独占锁:boolean tryAcquire,返回true表示加锁成功

共享锁:int tryAcquireShared,返回≥0代表成功

加锁失败

共同点:首次尝试加锁失败,调用addWaiter,并自旋(加锁->阻塞)

在自旋尝试加锁的过程中,若加锁成功,

独占模式下将当前结点设为头结点:head = node,失败则阻塞;

共享模式下,不仅会将当前结点设为头结点,还会在合适条件下(如还有剩余资源),调用doReleaseShared(),来继续唤醒其后继结点。

这也是两种模式最关键的不同

独占模式下,只有release时才会尝试unpark头结点的后继结点;

共享模式下,除release时会unpark外,线程在自旋时获取到锁后,也可能会unpark. 

个人理解:doReleaseShared()方法的自旋目的在于,在队列有结点出队时,快速通知下一下结点,使其unpark.

解锁

共同点:唤醒后继结点

独占模式下,head为-1则直接进行unparkSuccessor(h),随后方法结束

共享模式下,在doReleaseShared()中调用unparkSuccessor(h). 

代码如下,其中for循环的部分为doReleaseShared

4. 重要属性

waitStatus

static final class Node {
    volatile int waitStatus
}

-1:表明后继结点需要被唤醒;结点想要被park,也需要其前驱结点为-1。在上述加解锁的方法里被置为-1的时机有:

  • shouldParkAfterFailedAcquire(Node pred, Node node)中,对于pred<=0&&≠-1,将pred.ws置为-1

0:初始状态. 结点刚创建时的状态。在上述加解锁的方法里被置为0的时机有:

  • unparkSuccessor(Node node)中,如果ws<0, 将ws置为0【这一步我不知道有什么意义...】
  • doReleaseShared()中,调用unparkSuccessor(h)前,会将-1变为0。【有线程安全检查的作用,但不知道为何要置0】

        猜测:让即将被唤醒的线程成为头节点后,ws为0,这样后续结点可以多一次循环拿锁的机会,减小被park的概率

state


public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
    private volatile int state;
}

独占模式:0:空闲;1:被占有;≥1:被重入的次数

共享模式:state:资源数,0表示没有可用资源

二、ReentrantLock

ReentrantLock类结构

ReentrantLock源码

加锁:区分公平和非公平两种模式

    // ReentrantLock类
    public void lock() {
        sync.acquire(1);
    }

------------------------------非公平锁tryAcquire------------------------------
    // NonfairSync类
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
    
    // Sync类
    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;
        }

------------------------------公平锁tryAcquire------------------------------
        // FairSync类
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                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;
        }

解锁:公平与非公平模式都是调Sync的tryRelease

        // ReentrantLock类
        public void unlock() {
            sync.release(1);
        }

        // Sync类
        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;
        }

三、Semaphore

Semaphore类结构

Semaphore源码

加锁:区分公平和非公平两种模式

    // Semaphore类
    public void acquireUninterruptibly() {
        sync.acquireShared(1);
    }

------------------------------非公平锁tryAcquire------------------------------
    // NonfairSync类
    protected int tryAcquireShared(int acquires) {
        return nonfairTryAcquireShared(acquires);
    }
    
    // Sync类
    final int nonfairTryAcquireShared(int acquires) {
        for (;;) {
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                compareAndSetState(available, remaining))
                return remaining;
        }
    }

------------------------------公平锁tryAcquire------------------------------
        // FairSync类
        protected int tryAcquireShared(int acquires) {
            for (;;) {
                if (hasQueuedPredecessors())
                    return -1;
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

 解锁:公平与非公平模式都是调Sync的tryRelease

        // Semaphore类
        public void release(int permits) {
            if (permits < 0) throw new IllegalArgumentException();
            sync.releaseShared(permits);
        }

        // Sync类
        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }

四、CountDownLatch

countDownLatch在加锁时,只看state是否为0,无关公平与否, 因此只有公平模式

CountDownLatch类结构

CountDownLatch源码

---------------------------------加锁---------------------------------
    
    // CountDownLatch类
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    // Sync类
    protected int tryAcquireShared(int acquires) {
        return (getState() == 0) ? 1 : -1;
    }

---------------------------------解锁---------------------------------
        // CountDownLatch类
        public void countDown() {
            sync.releaseShared(1);
        }

        // Sync类
        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c - 1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }

ReentrantReadWriteLock

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值