当前位置: 首页 > article >正文

java CountDownLatch和CyclicBarrier

专栏系列文章地址:https://blog.csdn.net/qq_26437925/article/details/145290162

本文目标:

  1. 理解CountDownLatch和CyclicBarrier的使用,主要是复习Aqs

另外工作中用到CountDownLatch的地方还很多,一般是完成某些事情才能继续某项操作

  • 单词1: countdown
常见释义:[ˈkaʊntdaʊn][ˈkaʊntdaʊn]
n.	倒数读秒,倒计时(如发射宇宙飞船时); 大事临近的时期;
[例句]The countdown to Christmas starts here.
现在开始圣诞节倒计时。
[其他]	复数:countdowns
  • 单词2: latch
常见释义 英[lætʃ][lætʃ]
n.	插销; 门闩; 弹簧锁; 碰锁;
vt.	用插销插上; 用碰锁锁上;
[例句]He lifted the latch and opened the door.
他拉起门闩开了门。
[其他]	第三人称单数:latches 复数:latches 现在分词:latching 过去式:latched 过去分词:latched

目录

  • CountDownLatch (做减法,例子:六国逐一灭,秦统一)
    • CountDownLatch源码
      • countDownLatch.countDown()
      • countDownLatch.await()
  • CyclicBarrier(做加法,例子:召唤神龙,要收集到七龙珠)

CountDownLatch (做减法,例子:六国逐一灭,秦统一)

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

public class Main {

    public static void main(String[] args) throws Exception {
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "国,被灭");
                countDownLatch.countDown(); // count 减一
            }, "" + i).start();
        }
        // 一直等待,直到数字达到0,放行。
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName() + "\t **************秦帝国,统一华夏");

        closeDoor();
    }

    private static void closeDoor() throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"\t上完自习,离开教室");
                countDownLatch.countDown();
            }, "thread"+String.valueOf(i)).start();
        }
        // 等待,减少数字达到0,放行。
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName()+"\t **************班长最后关门走人");
    }
}

CountDownLatch源码

自定义Sync类继承AbstractQueuedSynchronizer,利用AQS自身的状态变量state代表数量(初始化的时候指定),countdown操作让状态只减1(具体是CAS操作compareAndSetState方法让state减少1)

public class CountDownLatch {
    /**
     * Synchronization control For CountDownLatch.
     * Uses AQS state to represent count.
     */
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        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;
            }
        }
    }

    private final Sync sync;

    public void countDown() {
        sync.releaseShared(1);
    }
  • unsafe.compareAndSwapInt方法
/**
    * Atomically sets synchronization state to the given updated
    * value if the current state value equals the expected value.
    * This operation has memory semantics of a {@code volatile} read
    * and write.
    *
    * @param expect the expected value
    * @param update the new value
    * @return {@code true} if successful. False return indicates that the actual
    *         value was not equal to the expected value.
    */
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

countDownLatch.countDown()

在这里插入图片描述

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

使用了Aqs的releaseShared方法

/**
    * Releases in shared mode.  Implemented by unblocking one or more
    * threads if {@link #tryReleaseShared} returns true.
    *
    * @param arg the release argument.  This value is conveyed to
    *        {@link #tryReleaseShared} but is otherwise uninterpreted
    *        and can represent anything you like.
    * @return the value returned from {@link #tryReleaseShared}
    */
public final boolean releaseShared(int arg) {
    // 如果 tryReleaseShared 返回 false,则 releaseShared 返回false
    // 如果 tryReleaseShared 返回 true, 执行 doReleaseShared
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}
  • countDownLatch 的tryReleaseShared方法:不断的while循环CAS操作让state减少1,如果成功就返回true;如果state已经等于0了,就直接返回false
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;
    }
}
  • aqs的doReleaseShared方法
  1. doReleaseShared会尝试唤醒head后继的代表线程,如果线程已经唤醒,则仅仅设置PROPAGATE状态
  2. 步骤一的“尝试唤醒head后继的代表线程”和“设置PROPAGATE状态”都是CAS操作,如果CAS失败,则会循环再次尝试
/**
* Release action for shared mode -- signals successor and ensures
* propagation. (Note: For exclusive mode, release just amounts
* to calling unparkSuccessor of head if it needs signal.)
*/
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()

在这里插入图片描述

/**

当前线程一直等待直到countDown到0,或者线程有interrupted异常抛出;

如果count本来就是0,那么该方法直接返回


* Causes the current thread to wait until the latch has counted down to
* zero, unless the thread is {@linkplain Thread#interrupt interrupted}.
*
* <p>If the current count is zero then this method returns immediately.
*
* <p>If the current count is greater than zero then the current
* thread becomes disabled for thread scheduling purposes and lies
* dormant until one of two things happen:
* <ul>
* <li>The count reaches zero due to invocations of the
* {@link #countDown} method; or
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread.
* </ul>
*
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
* <li>is {@linkplain Thread#interrupt interrupted} while waiting,
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* @throws InterruptedException if the current thread is interrupted
*         while waiting
*/
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);
}
  • CountdownLatch的tryAcquireShared方法:如果state==0返回1,否则返回-1;所以只要state不是0,if (tryAcquireShared(arg) < 0)条件就成立,则执行doAcquireSharedInterruptibly方法
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}
  • Aqs的doAcquireSharedInterruptibly方法

用当前线程构造一个共享模式Node,然后CAS操作加入CLH队列尾部,接着仍然是while(true)判断tryAcquireShared(arg),一定要是返回1,才会return,否则就是判断node的前驱节点,然后判断tryAcquireShared(arg)判断

/**
* Acquires in shared interruptible mode.
* @param arg the acquire argument
*/
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) {
                // 尝试获取资源
                int r = tryAcquireShared(arg);
                // 大于等于0,说明有资源获取
                if (r >= 0) {
                    // 把当前节点设置成head节点,并传播唤醒后面的节点。
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            // 这里和独占模式一样,如果没资源申请,封装节点,并park等待
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

CyclicBarrier(做加法,例子:召唤神龙,要收集到七龙珠)

A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other. The barrier is called cyclic because it can be re-used after the waiting threads are released.

  • cyclic: adj. 循环的; 周期的;
  • barrier: n. 屏障; 障碍物; 障碍; 阻力; 关卡; 分界线; 隔阂;
public class Main {

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("****召唤神龙****");
        });
        for (int i = 1; i <= 7; i++) {
            final int tempInt = i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"\t收集到第七颗龙珠:"+tempInt+"龙珠");
                try {
                    cyclicBarrier.await(); // 找到了,就继续等其它龙珠收集
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }

}

http://www.kler.cn/a/529080.html

相关文章:

  • Windows系统中Docker可视化工具对比分析,Docker Desktop,Portainer,Rancher
  • linux 函数 sem_init () 信号量、sem_destroy()
  • 实现网站内容快速被搜索引擎收录的方法
  • 【后端开发】字节跳动青训营Cloudwego脚手架
  • 【二叉搜索树】
  • [Collection与数据结构] B树与B+树
  • Spring AOP 入门教程:基础概念与实现
  • ASP.NET Core 启动并提供静态文件
  • 动态规划两个数组dp问题系列一>不相交的线
  • 一文讲解Java中的HashMap
  • 快速提升网站收录:如何设置网站标签?
  • pandas中的apply方法使用
  • 【漫话机器学习系列】074.异方差(Heteroscedasticity)
  • 【Linux】23.进程间通信(2)
  • 局域网文件互传:手机与电脑的便捷传输利器
  • 《Ollama与DeepSeek》
  • 力扣-链表-142 环形链表Ⅱ
  • AI(计算机视觉)自学路线
  • 【模拟汽笛ISIS】2022-9-15
  • BUUCTF [Black Watch 入群题]PWN1 题解
  • JAVA学习-练习试用Java实现“使用Swing创建一个带有按钮的窗口”
  • 一些计算机零碎知识随写(25年2月)
  • 论文和代码解读:RF-Inversion 图像/视频编辑技术
  • 7 与mint库对象互转宏(macros.rs)
  • 快速提升网站收录:利用网站分析工具
  • 比较热门的嵌入式项目