AQS共享模式之Semaphore源码解析

1.Semaphore源码解析

1.1概述

  • Semaphore信号量常用来做限流使用,在高并发场景下用于控制同一时刻对共享资源的访问。可以用于商品的秒杀等场景。
  • Semaphore的内部是基于AQS的共享锁来实现的。使用了类似ReentrantLock的内部结构,使用了内部类Sync(继承AQS),然后两个实现类NonfairSync(非公平逻辑)FairSync(公平的逻辑)
    • 内部的资源使用的还是AQS的state (volatile int state)属性,当acquire()时,state会+1,当release()时,state - 1(常用的就是1,也可以指定)

1.2使用流程图

在这里插入图片描述

1.3内部结构

1.3.1静态抽象内部类Sync

abstract static class Sync extends AbstractQueuedSynchronizer {
    
    
        private static final long serialVersionUID = 1192457210091910933L;
		
        
        //构造方法 传入int值,赋值给内部的state。
        Sync(int permits) {
    
    
            setState(permits);
        }
		
        //获取state
        final int getPermits() {
    
    
            return getState();
        }
		
    	//非公平模式下获取信号量 上来直接抢占锁,不会判断队列中是否有节点排队
        final int nonfairTryAcquireShared(int acquires) {
    
    
            //自旋 + CAS
            for (;;) {
    
    
                //获取当前的state
                int available = getState();
                //state - acquires 赋值给remaining
                int remaining = available - acquires;
                //判断减完之后的state < 0 那么直接返回一个负数,代表获取state失败
                //条件二:state > 0 使用CAS方式尝试获取state,获取成功返回state的值,
                //失败继续自旋获取。
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
		
        //CAS + 自旋 尝试将state的值加上releases。
        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;
            }
        }
		
    
    	//CAS + 自旋 将state的值减去reductions。
        final void reducePermits(int reductions) {
    
    
            for (;;) {
    
    
                int current = getState();
                int next = current - reductions;
                if (next > current) // underflow
                    throw new Error("Permit count underflow");
                if (compareAndSetState(current, next))
                    return;
            }
        }
		
    	//CAS + 自旋 重置state为 0
        final int drainPermits() {
    
    
            for (;;) {
    
    
                int current = getState();
                if (current == 0 || compareAndSetState(current, 0))
                    return current;
            }
        }
    }

1.3.2Sync实现类NonfairSync

static final class NonfairSync extends Sync {
    
    
    private static final long serialVersionUID = -2694183684443567898L;
	
	// 构造方法,调用父类的构造方法
    NonfairSync(int permits) {
    
    
        super(permits);
    }
	
    /*
     *  @return ( > 0 表示获取信号量(锁)成功,< 0表示获取信号量失败) 
     */
    protected int tryAcquireShared(int acquires) {
    
    
        return nonfairTryAcquireShared(acquires);
    }
}

1.3.3Sync实现类FairSync(公平模式)

公平模式下,先检测同步队列中是否有排队的Node,如果有排队的Node直接进入同步队列排队,否则CAS更新state的值

1.3.4核心方法之acquire()

public void acquire() throws InterruptedException {
    
    
        sync.acquireSharedInterruptibly(1);
}

//          ||
//          ||
//          ||
//          \/

//AQS的共享方法。
public final void acquireSharedInterruptibly(int arg)
    throws InterruptedException {
    
    
    
    //当前线程标志位处于中断状态,直接抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    
    //自旋 + CAS获取信号量,获取成功返回直接走业务逻辑,失败则构造节点入队阻塞。
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}


/*
 *  CAS + 自旋尝试获取通行证,获取成功返回 >= 0的值,获取失败返回 < 0 的值。
 */
protected int tryAcquireShared(int acquires) {
    
    
    //自旋
    for (;;) {
    
    
        /*
         * 判断当前AQS阻塞队列内是否有等待者线程,如果有直接返回-1,表示当前acquire的操作		 * 需要进入到等待队列
         * (因为这里我们解析的是公平锁源码,所以这里会判断,非公平锁直接上来就抢)
         */   
        if (hasQueuedPredecessors())
            return -1;
        
        /*
         *  执行到这里,有哪几种情况?
         *  1.调用acquire时,AQS同步队列内没有其他等待者节点
         *  2.当前节点 在同步队列中是headNext节点。
         */
        
        //获取state的值,
        int available = getState();
        
		//remaining表示当前线程获取完信号量之后还剩余的信号量。
        int remaining = available - acquires;
        
        /*
         * 条件一:remining < 0表示线程获取通行证失败
         * 条件二:前置条件 remaining >= 0, 使用CAS更新state的值。
         */
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

 /*
  *  将当前线程构造为一个Node进入同步队列,并且
  */
private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
    
    
    	//构造节点(共享模式)并入同步队列
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
    
    
            //自旋
            for (;;) {
    
    
                //获取前驱节点
                final Node p = node.predecessor();
                //当前节点时head.next节点,尝试获取信号量
                if (p == head) {
    
    
                    //返回值表示的是state - arg 后的state值
                    int r = tryAcquireShared(arg);
                    //大于等于0,表示获取成功,然后
                    if (r >= 0) {
    
    
                        //设置当前节点为头节点,并进行唤醒后继节点的操作
                        setHeadAndPropagate(node, r);
                        p.next = null; 
                        failed = false;
                        return;
                    }
                }
                /*
                 *  为当前Node在同步队列中找到一个状态合法的前驱Node,
                 *  然后挂起当前线程。
                 */
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
    
    
            if (failed)
                cancelAcquire(node);
        }
}

1.3.5acquire简单流程图

在这里插入图片描述

1.3.6核心方法之release()

public void release() {
    
    
    sync.releaseShared(1);
}

public final boolean releaseShared(int arg) {
    
    
     	/*
     	 * tryReleaseShared() CAS + 自旋方式释放资源,大概率都会成功
     	 */
            
        if (tryReleaseShared(arg)) {
    
    
            //唤醒获取资源失败的线程
            doReleaseShared();
            return true;
        }
        return false;
}

//此方法详细讲解见CountDownLatch源码解析
private void doReleaseShared() {
    
    
    for (;;) {
    
    
        Node h = head;
        if (h != null && h != tail) {
    
    
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
    
    
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            
                //唤醒head.next节点
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                
        }
        if (h == head)                   
            break;
    }
}

1.3.7release()简单流程图在这里插入图片描述

Guess you like

Origin blog.csdn.net/qq_46312987/article/details/121784512