AQS之CountDownLatch、Semaphore、CyclicBarrier

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状态,除非满足下面两个条件:

  1. count到0
  2. 线程中断
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

原文地址:https://www.cnblogs.com/studentytj/p/11324371.html

时间: 2024-10-24 23:23:06

AQS之CountDownLatch、Semaphore、CyclicBarrier的相关文章

1.3.4 并发工具类CountDownLatch/Semaphore/CyclicBarrier/FutureTask

CountDownLatch的2个用途: 1. 所有线程都到达相同的起跑线后,再一起开始跑(并非同时开始,而是队列中一个唤醒另一个)[此情况需到达起跑线后再调用await()等待其他线程] 2. 所有线程都到达终点(执行完)后,再一起庆祝 (并非同时开始,而是队列中一个唤醒另一个)[此情况需到达起终点后再调用await()等待其他线程] package com.study.concurrent_utils; import java.util.concurrent.CountDownLatch;

CountDownLatch,CyclicBarrier,Semaphore

CountDownLatch是倒数,doneSignal = new CountDownLatch(LATCH_SIZE);赋初值后,在主线程中等待doneSignal.await();其它线程中,每完成一个就减一doneSignal.countDown();减到0时主线程继续. CyclicBarrier是正数,cb = new CyclicBarrier(SIZE);主线程中开启各子线程,子线程调用cb.await()进行等待;cb计数count会加一,等于SIZE时会继续所有等待线程. S

并发编程(七)——AbstractQueuedSynchronizer 之 CountDownLatch、CyclicBarrier、Semaphore 源码分析

这篇,我们的关注点是 AQS 最后的部分,共享模式的使用.本文先用 CountDownLatch 将共享模式说清楚,然后顺着把其他 AQS 相关的类 CyclicBarrier.Semaphore 的源码一起过一下. CountDownLatch CountDownLatch 这个类是比较典型的 AQS 的共享模式的使用,这是一个高频使用的类.使用方法在前面一篇文章中有介绍 并发编程(二)—— CountDownLatch.CyclicBarrier和Semaphore 使用例子 我们看下 Do

java中的JUC组件(Semaphore、CountDownLatch、CyclicBarrier)

目录 1.简介 2.Semaphore 3.CountDownLatch 4.CyclicBarrier 1.简介 Semaphore.CountDownLatch.CyclicBarrier 这三个工具类都是用于并发控制的操作,底层都是基于AQS去实现的: Semaphore(信号量): 提供一个竞争资源处理的工具,当系统内有足够的信号量事,线程可以去获取信号量执行操作,当信号量资源被使用完后,需要等待资源释放后后续线程(资源竞争)才能够执行: CountDownLatch(闭锁):可以理解为

JUC常用同步工具类——CountDownLatch,CyclicBarrier,Semaphore

在 JUC 下包含了一些常用的同步工具类,今天就来详细介绍一下,CountDownLatch,CyclicBarrier,Semaphore 的使用方法以及它们之间的区别. 一.CountDownLatch 先看一下,CountDownLatch 源码的官方介绍. 意思是,它是一个同步辅助器,允许一个或多个线程一直等待,直到一组在其他线程执行的操作全部完成. public CountDownLatch(int count) { if (count < 0) throw new IllegalAr

Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier和Semaphore,今天我们就来学习一下这三个辅助类的用法. 以下是本文目录大纲: 一.CountDownLatch用法 二.CyclicBarrier用法 三.Semaphore用法 若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/dolphin0520/p/3920397.htm

CountDownLatch、CyclicBarrier和Semaphore

CountDownLatch: 允许N个线程等待其他线程完成执行.无法进行重复使用,只能用一次. 比如有2个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了. public class Test { public static void main(String[] args) { final CountDownLatch latch = new CountDownLatch(2); new Thread(){ public void r

并发工具类:CountDownLatch、CyclicBarrier、Semaphore

在多线程的场景下,有些并发流程需要人为来控制,在JDK的并发包里提供了几个并发工具类:CountDownLatch.CyclicBarrier.Semaphore. 一.CountDownLatch 1 import java.util.concurrent.CountDownLatch; 2 3 4 public class CountDownLatchTest 5 { //设置N为2 6 static CountDownLatch c = new CountDownLatch(2); 7 p

CountDownLatch,CyclicBarrier,Semaphore的使用

什么时候使用CountDownLatch CountDownLatch原理和示例 Semaphore信号量的原理和示例 CyclicBarrier的用法 CyclicBarrier 和 CountDownLatch 在用法上的不同: 1.CountDownLatch 适用于一组线程和另一个主线程之间的工作协作.一个主线程等待一组工作线程的任务完毕才继续它的执行是使用 CountDownLatch 的主要场景:CyclicBarrier 用于一组或几组线程,比如一组线程需要在一个时间点上达成一致,

Java并发之CountDownLatch、CyclicBarrier和Semaphore

CountDownLatch 是能使一组线程等另一组线程都跑完了再继续跑:CyclicBarrier 能够使一组线程在一个时间点上达到同步,可以是一起开始执行全部任务或者一部分任务. 这次说一下 JUC 中的同步器三个主要的成员:CountDownLatch.CyclicBarrier 和 Semaphore(不知道有没有初学者觉得这三个的名字不太好记).这三个是 JUC 中较为常用的同步器,通过它们可以方便地实现很多线程之间协作的功能.(下面的代码出自 JDK 文档) CountDownLat