concurrent-6-AQS-Semaphore

本文详细解析了Semaphore的工作原理,包括资源的获取与释放过程。通过Semaphore#acquire与Semaphore#release方法,利用AQS框架实现了线程间的同步控制。具体介绍了nonfairTryAcquireShared方法如何进行资源竞争,以及doAcquireSharedInterruptibly方法处理中断请求。

Semaphore#acquire

 public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);   //请求资源
    }

AQS#acquireSharedInterruptibly

 public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)  //获取资源失败
            doAcquireSharedInterruptibly(arg);   //循环cas 获取资源
    }

Semaphore#nonfairTryAcquireShared

   final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();  //获取可用状态
                int remaining = available - acquires;
                if (remaining < 0 ||     //如果剩余状态大于0 则尝试cas操作 否则 return
                    compareAndSetState(available, remaining)) //cas获取资源
                    return remaining;
            }
        }

AQS#doAcquireSharedInterruptibly

//doAcquireSharedInterruptibly 类似,响应中断
private void doAcquireShared(int arg) {
        final Node node = addWaiter(Node.SHARED);  //将share节点入队列
        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);
        }
    }

Semaphore#release

 public void release() {
        sync.releaseShared(1);  //释放资源
    }

Semaphore#tryReleaseShared

protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;  //释放资源则可用数增加 releases
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))   //cas操作释放资源
                    return true;
            }
        }

AQS#doReleaseShared

private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. But if it does not, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        for (;;) {
            Node h = head;
            //最终状态 signal = 》0 = 》 PROPAGATE  经过一步0的操作时因为unparkSuccessor 中会有cas设置为0状态
            if (h != null && h != tail) {
                int ws = h.waitStatus;  //获取头节点的状态
                if (ws == Node.SIGNAL) {  //如果节点的状态为唤醒,则置为0
                    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;
        }
    }
### AQS的主要特征和核心机制 AbstractQueuedSynchronizer(AQS)是Java并发编程中的核心同步框架,其设计目标是为构建锁和同步器提供统一的基础设施。AQS通过内部状态管理、线程排队和阻塞/唤醒机制来实现高效的线程同步控制。 #### 核心特征 1. **状态管理** AQS内部维护了一个名为`state`的整型变量,用于表示同步状态。该状态可以通过原子操作进行修改,例如获取锁或释放锁时对`state`值的增减操作。这种状态管理机制确保了多线程环境下的数据一致性[^3]。 2. **CLH队列模型** AQS使用一个基于CLH(Craig, Landin, and Hagersten)锁算法的变种来管理等待线程。当线程尝试获取锁失败时,它会被封装成一个节点(Node),并被加入到一个FIFO的同步队列中,随后进入等待状态,直到被前驱节点唤醒。这种设计保证了线程调度的公平性和高效性[^4]。 3. **支持独占与共享模式** AQS支持两种基本的同步模式:独占模式(Exclusive Mode)和共享模式(Shared Mode)。在独占模式下,一次只能有一个线程获得同步状态,如ReentrantLock;而在共享模式下,多个线程可以同时持有同步状态,如Semaphore和CountDownLatch[^2]。 4. **灵活的扩展能力** AQS本身是一个抽象类,提供了大量可重写的方法供子类自定义同步逻辑。开发者只需关注如何实现特定的同步语义,而无需处理底层复杂的线程调度和状态管理问题。这使得AQS成为许多高级同步组件的基础,如ReentrantLock、ReadWriteLock等[^1]。 #### 核心机制 1. **线程竞争与排队** 当线程尝试获取同步状态失败时,它会将自身封装为一个Node对象,并将其添加到同步队列的尾部。这一过程是通过CAS(Compare and Swap)操作完成的,以确保线程安全地入队[^4]。 2. **自旋与阻塞** 在同步队列中,线程会不断检查自己是否具备获取同步状态的资格(通常是当前节点是否为头节点的下一个节点)。如果条件不满足,则线程会进入阻塞状态,直到被前驱节点唤醒。这种自旋与阻塞结合的方式,既减少了上下文切换的开销,又避免了长时间的空转等待[^2]。 3. **唤醒机制** 当持有同步状态的线程释放资源时,它会更新同步状态并唤醒同步队列中的后继节点,使其有机会重新竞争同步状态。这一机制确保了线程能够按照FIFO顺序依次获取资源,从而实现了良好的并发控制效果。 4. **钩子方法** AQS定义了一系列受保护的钩子方法(如`tryAcquire`、`tryRelease`、`tryAcquireShared`等),这些方法由子类具体实现,用于定义不同的同步策略。例如,ReentrantLock在`tryAcquire`中实现了可重入锁的逻辑,而Semaphore则在其中实现了许可数的扣减操作[^1]。 #### 示例代码 以下是一个简化的AQS实现示例,展示了如何通过继承AQS并重写相关方法来实现一个简单的锁: ```java import java.util.concurrent.locks.AbstractQueuedSynchronizer; public class SimpleLock extends AbstractQueuedSynchronizer { // 尝试获取锁 protected boolean tryAcquire(int acquires) { if (compareAndSetState(0, 1)) { setExclusiveOwnerThread(Thread.currentThread()); return true; } return false; } // 尝试释放锁 protected boolean tryRelease(int releases) { if (getState() == 0) throw new IllegalMonitorStateException(); setExclusiveOwnerThread(null); setState(0); return true; } public void lock() { acquire(1); } public void unlock() { release(1); } public boolean tryLock() { return tryAcquire(1); } } ``` 上述代码展示了一个最基本的锁实现,通过重写`tryAcquire`和`tryRelease`方法,结合AQS提供的队列管理和线程调度功能,实现了线程间的互斥访问。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值