AQS(AbstractQueuedSynchronizer)源代码分析(三)——共享的aquired和release

     本文是在AQS(AbstractQueuedSynchronizer)源代码分析(一)AQS(AbstractQueuedSynchronizer)源代码分析(二)两篇文章的基础上编写的。关于shouldParkAfterFailedAcquire,addWaiter,parkAndCheckInterrupt,park,unpark,unparkSuccessor方法,如果不清楚,可以参考前两篇文章,本文不再做说明。

     AQS中的非共享模式与共享模式的区别在于,非共享模式每次只能有一个线程执行被锁定的内容,即目标语句或者目标方法。比如ReentrantLock类,在lock方法锁定的范围内,只会同时有一个线程在执行。而共享模式则是可以多个线程同时执行被锁定内容的。典型的应用类就是Semaphore。在设定初始信号量的值之后,比如是5;那么就会有五个线程同时执行。  

 一、共享的acquire方法

public final void acquireShared(int arg) {
	//这个方法由子类实现。这里使用Semaphore.NonfairSync类中的tryAcquireShared进行说明
	if (tryAcquireShared(arg) < 0) 
	
		//返回值小于0,则表示sate值已经被使用完,即已经有5个线程在运行。其他线程不能再进入了
		doAcquireShared(arg); 
}

//类Semaphore.NonfairSync
protected int tryAcquireShared(int acquires) {
	return nonfairTryAcquireShared(acquires);
}
		
final int nonfairTryAcquireShared(int acquires) {

/**
   *   当使用Semaphore时会有如下的代码:
   *   Semaphore semaphore = new Semaphore(5);
   *   semaphore.acquire();
   *   		……
   *   semaphore.release();
   *   
   *   这里的getSate,如果是第一次进行semaphore.acquire()的话,
   *   getSate返回5,即我们设定的初始值
   *   每一次semaphore.acquire()都会对将getSate的返回值进行减少,
   *   即:int remaining = available - acquires,这段代码的含义
   */
	for (;;) {
		int available = getState(); //获取sate状态
		int remaining = available - acquires; 
		if (remaining < 0 ||
			compareAndSetState(available, remaining))
			return remaining;  //这里会返回本次acquire之后,还剩余的sate值
	}
}

上面的代码演示了Semaphore是如何进行信号量的获取操作的。当信号量获取失败之后,就会执行doAcquireShared(arg)方法。看一下doAcquireShared(arg)方法的代码:

private void doAcquireShared(int arg) {
	final Node node = addWaiter(Node.SHARED); //向队列增加节点,追加的node是共享模式的node
	boolean failed = true;
	try {
		boolean interrupted = false;
		for (;;) {  //死循环
			final Node p = node.predecessor(); //获取当前节点的prevNode
			if (p == head) {
				int r = tryAcquireShared(arg);  //如果是prevNode节点如果是head节点,则再次尝试acquire
				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);
	}
}

这段代码的执行逻辑与非共享模式的执行逻辑相似,这里不赘述。我们重点关注的是“当再次尝试,获取成功之后的setHeadAndPropagate方法的处理”。这个方法的处理是与非共享模式不一样的。setHeadAndPropagate的代码:

private void setHeadAndPropagate(Node node, int propagate) {
	Node h = head; // Record old head for check below
	
	//替换head节点。因为head表示当前正在执行的节点。
	//节点已经获取成功,则将会被执行,所以需要替换head
	setHead(node); 
	 
	if (propagate > 0 || h == null || h.waitStatus < 0 ||
		(h = head) == null || h.waitStatus < 0) {
		Node s = node.next;
		if (s == null || s.isShared()) //获取当前节点的下一个节点,判断是否是共享模式的节点
		
			//如果是共享模式的节点,则唤醒nextNode。doReleaseShared方法其实就是semaphore.release()
			//的内部实现。具体的实现,下文release方法中进行说明
			doReleaseShared(); 
	}
}
可以看到,在非共享模式中,如果获取成功后,仅仅只是直接替换了head节点,并没有唤醒nextNode节点的操作;而对于共享模式,则进行了唤醒操作。所以这个唤醒的操作将是共享模式的核心。


二、共享的release方法

代码:

public final boolean releaseShared(int arg) {
	if (tryReleaseShared(arg)) {  //尝试释放,如果释放成功,则执行doReleaseShared
		doReleaseShared();
		return true;
	}
	return false;
}

类Semaphore.Sync中的tryReleaseShared(arg)方法的实现如下:

protected final boolean tryReleaseShared(int releases) {
	for (;;) {
		int current = getState();
		int next = current + releases; //acquire是减少,release就是增加
		if (next < current) // overflow
			throw new Error("Maximum permit count exceeded");
		if (compareAndSetState(current, next))
			return true;
	}
}

接下来重点看一下doReleaseShared()方法:

private void doReleaseShared() {
	 
	for (;;) {
		Node h = head;
		if (h != null && h != tail) {
			int ws = h.waitStatus; //获取head节点的状态
			
			//判断head节点的状态,如果head节点的状态是SIGNAL,
			//表示head节点存在nextNode,需要唤醒nextNode
			if (ws == Node.SIGNAL) {   
			
				//先尝试修改head节点的状态,如果成功,则唤醒nextNode,如果失败,则继续尝试
				if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) 
					continue;            // loop to recheck cases
				unparkSuccessor(h);
			}
			else if (ws == 0 &&  
					 !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
				continue;     
				
			//如果head节点的状态是0,则尝试修改为PROPAGATE;
			//PROPAGATE表示下一个节点可以无条件的进行acquire,也就是下一次acquire一定会成功,即信号量充足
			//如果head节点的状态是PROPAGATE,则不做任何处理,直接退出循环返回。
		}
		if (h == head) //跳出循环
			break;
	}
}

以上基本就是AQS的共享模式的实现。对于doReleaseShared()方法中对于head节点的SIGNAL状态判断,很多人可能会有疑惑。疑惑在于:head节点什么时候才会为SIGNAL状态呢?

      对于这个疑惑,首先先要知道SIGNAL状态表示什么。SIGNAL状态表示节点有nextNode,即有后续处理节点。在当前的节点处理完之后,需要唤醒nextNode。以Semaphore类举例:

     当我们的5个信号量都用完的时候,有新的线程进入(下文称作newThread)。此时,newThread就会被挂起。这个挂起的过程需要经过两个个步骤:

1、在node队列增加一个node,且这个node是共享模式的

2、判断新增加的node的prevNode(前节点)是否为SIGNAL状态。如果是SIGNAL状态,则直接挂起线程;如果不是SGINAL状态,则先将前一个node的状态修改SIGNAL状态,再挂起线程。

关于上面两个步骤的实现在shouldParkAfterFailedAcquire方法和doAcquireShared方法中












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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值