AQS之CountDownLatchSemaphoreCyclicBarrier
Posted studentytj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AQS之CountDownLatchSemaphoreCyclicBarrier相关的知识,希望对你有一定的参考价值。
CountDownLatch
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
通常情况下,countDown如下调用
CountDownLatch countDownLatch = new CountDownLatch(1);
countDownLatch.countDown();
countDownLatch.await();
看一下countDown方法:
public void countDown()
sync.releaseShared(1);
AQS中releaseShared方法如下:
public final boolean releaseShared(int arg)
if (tryReleaseShared(arg))
doReleaseShared();
return true;
return false;
CountDownLatch中tryReleaseShared方法如下:
// 方法判断许可如果减1之后是否为0,如果为0的话就执行doReleaseShared()方法。
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;
来看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;
if (h != null && h != tail)
int ws = h.waitStatus;
if (ws == Node.SIGNAL)
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;
不过尴尬的是,CountDownLatch这里未做任何事情。
再看一下await()方法:
await方法会让当前线程进入wait状态,除非满足下面两个条件:
- count到0
- 线程中断
public void await() throws InterruptedException
sync.acquireSharedInterruptibly(1);
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
tryAcquireShared方法如下:
protected int tryAcquireShared(int acquires)
return (getState() == 0) ? 1 : -1;
所以,当state不是0的时候进入doAcquireSharedInterruptibly方法。
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try
for (;;)
final Node p = node.predecessor();
if (p == head)
// 只有当state为0时r为1
int r = tryAcquireShared(arg);
if (r >= 0)
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
// 如果state不为0,该线程会进入wait状态
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
finally
if (failed)
cancelAcquire(node);
CountDownLatch文档中有一句非常重要的话:
Memory consistency effects: Until the count reaches zero, actions in a thread prior to calling countDown()
happen-before actions following a successful return from a corresponding await()
in another thread
大意是一个线程countdown()之前的操作happens-before另一个线程中await()之后的操作。
Semaphore
Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource.
Semaphore主要用来限制获取资源的线程数。
Actions in a thread prior to calling a "release" method such as release() happen-before actions following a successful "acquire" method such as acquire() in another thread
内存语义:release() happen-before acquire()之前
启一个springboot项目,写一个方法:
@RequestMapping("/test/semaphore")
@ResponseBody
public void test() throws InterruptedException
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 7; i++)
int finalI = i;
new Thread(()->
try
semaphore.acquire();
System.err.println(Thread.currentThread() + "获取了许可" + semaphore.availablePermits());
catch (InterruptedException e)
e.printStackTrace();
, "线程" + i).start();
new Thread(()->
try
Thread.sleep(10000);
catch (InterruptedException e)
e.printStackTrace();
System.err.println(Thread.currentThread() + "要释放许可" + semaphore.availablePermits());
semaphore.release();
, "线程7").start();
一次输出如下:
Thread[线程1,5,main]获取了许可4
Thread[线程0,5,main]获取了许可3
Thread[线程3,5,main]获取了许可2
Thread[线程4,5,main]获取了许可0
Thread[线程2,5,main]获取了许可0
Thread[线程7,5,main]要释放许可0
Thread[线程5,5,main]获取了许可0
会发现,线程5获取许可之前是先等线程7释放许可。
至于线程6会因为由于许可为0,进入等待状态。直到有线程释放许可,来调用unparkSuccessor。
CyclicBarrier
A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.
Actions in a thread prior to calling await() happen-before actions that are part of the barrier action, which in turn happen-before actions following a successful return from the corresponding await() in other threads.
内部类Generation只有一个属性broken(默认false)
我们发现,await()方法如下:
public int await() throws InterruptedException, BrokenBarrierException
try
return dowait(false, 0L);
catch (TimeoutException toe)
throw new Error(toe); // cannot happen
进入dowait方法:
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException
final ReentrantLock lock = this.lock;
lock.lock();
try
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted())
breakBarrier();
throw new InterruptedException();
// 来一个线程count减1,如果index为0,就会翻车
int index = --count;
if (index == 0) // tripped
boolean ranAction = false;
try
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
finally
if (!ranAction)
breakBarrier();
// 没翻车(broken,interrupted,timed out)的话就执行下面的逻辑
// loop until tripped, broken, interrupted, or timed out
for (;;)
try
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
catch (InterruptedException ie)
if (g == generation && ! g.broken)
breakBarrier();
throw ie;
else
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L)
breakBarrier();
throw new TimeoutException();
finally
lock.unlock();
下面进入trip.await()方法
public final void await() throws InterruptedException
if (Thread.interrupted())
throw new InterruptedException();
// 往等待队列加入节点Node
Node node = addConditionWaiter();
// 这里释放AQS中的state, 如果释放失败,会将node的waitstatus置为CANCELLED,这是传参node的唯一用处
int savedState = fullyRelease(node);
int interruptMode = 0;
// 如果node有next就肯定返回true
while (!isOnSyncQueue(node))
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
// 如果当前线程
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
进入addConditionWaiter()
private Node addConditionWaiter()
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION)
unlinkCancelledWaiters();
t = lastWaiter;
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
假如5个线程按顺序进入await(),则此时,trip这个ConditionObject上firstWaiter==lastWaiter==new Node("线程0对应的线程", Node.CONDITION)
同时,因为dowait方法中的lock.lock(),AQS的同步队列如下:
head节点--》线程1--》线程2--》线程3--》线程4(tail)
等待队列: t0
当释放线程0的锁之后,唤醒线程1,将线程1加入等待队列,线程2/3也加入等待队列。此时同步队列还剩下线程4。此时队列情况是:
同步队列:head节点
等待队列:t0->t1->t2->t3
到了最后一个线程4执行的时候,index==0,执行nextGeneration,会signalAll trip这个Condition上的所有等待线程。所以经过signalAll之后,队列情况变成了:
同步队列:head->t0->t1->t2->t3
等待队列:空
此时线程4运行,释放锁之后唤醒同步队列上的第一个节点t0
以上是关于AQS之CountDownLatchSemaphoreCyclicBarrier的主要内容,如果未能解决你的问题,请参考以下文章