首页 > 其他分享 >计数器 CountDownLatch

计数器 CountDownLatch

时间:2024-09-09 15:29:05浏览次数:6  
标签:int state util 计数器 线程 CountDownLatch latch

CountDownLatch

Semaphore 更加适合用于控制对有限资源的访问,特别是当你需要允许一定数量的线程同时访问资源时

CountDownLatch 更加适合用于协调多个线程的完成状态,确保在某些操作完成后再执行后续操作

它用于协调多个线程的执行,使得某些操作必须等到其他操作完成后才能继续进行,常用方法如下

CountDownLatch latch = new CountDownLatch(3); // 创建一个计数器,计数器的值为 3(需要等待的线程数量或事件数量是 3)
latch.countDown(); // 减少计数器的值,每次调用都会将计数器 -1
latch.await(); // 在哪个线程调用 await,哪个线程就会阻塞,直到计数器为 0 才恢复运行

用法示例

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个 CountDownLatch,初始计数为3
        CountDownLatch latch = new CountDownLatch(3);

        // 启动3个线程,每个线程完成后调用countDown()
        for (int i = 0; i < 3; i++) {
            new Thread(new MyThread(latch)).start();
        }

        // 主线程等待,直到计数器减到零
        latch.await();

        System.out.println("All workers have finished their tasks.");
    }
}

class MyThread implements Runnable {
    private CountDownLatch latch;

    public Worker(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            // 模拟任务处理
            Thread.sleep(1000);
            System.out.println("Worker finished");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            // 完成任务后调用 countDown
            latch.countDown();
        }
    }
}

原理

  • state 初始设置了值,当主线程调用 await() 发现 state 不是 0,获取不到锁,主线程就进入队列阻塞
  • 每调用一次 countDown() 方法,state -1,当减后是0,需要唤醒等待的线程
  • 需要注意的是,当 state 已经是 0,调用 countDown() 方法是无意义的

构建一个计数器

// 构造方法
public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    // new 了一个 Sync,和前端的所有锁都是一样的,这个 Sync 继承了 AQS
    this.sync = new Sync(count);
}

// Sync 构造方法
Sync(int count) {
    setState(count); // 调用父类 AQS 的方法,修改 state 的值
}

// java.util.concurrent.locks.AbstractQueuedSynchronizer#setState
protected final void setState(int newState) {
    state = newState;
}

计数器-1

// java.util.concurrent.CountDownLatch#countDown
public void countDown() {
    sync.releaseShared(1); // AQS 的方法
}

// java.util.concurrent.locks.AbstractQueuedSynchronizer#releaseShared
// 1,返回值没意义,因为在 countDown 方法中没根据返回值做什么处理
// 2,tryReleaseShared() 方法返回 true:说明是 state - 1 后为 0,需要唤醒等待线程
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) { // 模板方法
        doReleaseShared(); // 唤醒等待线程
        return true;
    }
    return false;
}

// java.util.concurrent.locks.AbstractQueuedSynchronizer#tryReleaseShared
protected boolean tryReleaseShared(int arg) {
    throw new UnsupportedOperationException();
}

// java.util.concurrent.CountDownLatch.Sync#tryReleaseShared
protected boolean tryReleaseShared(int releases) {
    for (;;) {
        int c = getState();
        if (c == 0) // 已经是 0,返回 false
            return false;
        int nextc = c-1;
        if (compareAndSetState(c, nextc))
            return nextc == 0; // 仅当 state - 1 后 state 是 0 才返回 true
    }
}

await() 方法

这个就更简单了

// java.util.concurrent.CountDownLatch#await()
public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

// java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireSharedInterruptibly
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0) // 获取锁,只会返回 1 或 -1(-1 表示不能获取锁,要阻塞)
        doAcquireSharedInterruptibly(arg); // 前面分析过好多次了,进入队列,维护链表,挂起线程
}

// java.util.concurrent.CountDownLatch.Sync#tryAcquireShared
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1; // 当 state 不是 0,返回 -1
}

标签:int,state,util,计数器,线程,CountDownLatch,latch
From: https://www.cnblogs.com/cyrushuang/p/18404620

相关文章

  • 循环计数器/循环栅栏/循环屏障 CyclicBarrier
    CyclicBarrier和CountDownLatch有点类似,主要区别是CyclicBarrier可以重用,常用方法如下:CyclicBarrierbarrier=newCyclicBarrier(3);//表示条件为:要有3个线程达到屏障(未指定屏障动作)barrier.await();//如果没有3个线程到达屏障,当前线程就阻塞,直到有3个线程达到......
  • CountDownLatch源码剖析
    CountDownLatch门闩,他可以让多个线程都阻塞在⼀个地⽅,直到所有线程任务都执⾏完成。测试案例:先让子线程执行完了,再让主线程执行publicclassCountDownLatchDemo{publicstaticvoidmain(String[]args){CountDownLatchDemodemo=newCountDownLatchDem......
  • SMART PLC高速计数器频率测量功能块(脉冲频率测量功能块)
    PTO和高速计数器组合实验请参考下面文章链接:SMARTPLC脉冲输出指令PLS应用(PTO和高速计数器组合实验)-CSDN博客文章浏览阅读56次。200SMARTPLC如何实现可调频率可调占空比PWM输出200smart_PLC如何实现可调频率可调占空比PWM输出_200smartpwm-CSDN博客本文介绍了如何在SMART......
  • 地平线—征程2(Journey 2-J2)芯片详解(25)—PMU+系统计数器
    写在前面本系列文章主要讲解地平线征程2(Journey2-J2)芯片的相关知识,希望能帮助更多的同学认识和了解征程2(Journey2-J2)芯片。若有相关问题,欢迎评论沟通,共同进步。(*^▽^*)错过其他章节的同学可以电梯直达目录↓↓↓地平线—征程2(Journey2-J2)芯片详解——目录-CSDN博客9......
  • 面试题:在Java中,JVM(Java虚拟机)的内存模型是如何设计的?请详细解释堆(Heap)、栈(Stack)、方法
    面试题:在Java中,JVM(Java虚拟机)的内存模型是如何设计的?请详细解释堆(Heap)、栈(Stack)、方法区(MethodArea)以及程序计数器(ProgramCounterRegister)的作用和它们之间的关系。更多答案在这里,手机或电脑浏览器就可以打开,面霸宝典【全拼音】.com这里可以优化简历,模拟面试,企业项......
  • CountDownLatch
    importlombok.SneakyThrows;importjava.util.Date;importjava.util.concurrent.*;/*main上锁3线程1获得锁线程2获得锁线程3获得锁线程1释放锁2线程2释放锁1线程3释放锁0main解锁0**/publicclassT{@SneakyThrowspublicstaticvoidmain(String[......
  • 040_java.util.concurrent.CountDownLatch
    简单介绍CountDownLatch的通常用法和Thread.join()有点类似,等待其它线程都完成后再执行主任务。允许一个或多个线程等待其它线程的操作执行完毕后再执行后续的操作。先看看怎么用:publicclassCountDownLatchTest{publicstaticvoidmain(String[]args)throwsIn......
  • 数据结构 顺序队列(计数器版)
    在实现循环队列时,为了区分队列为空和队列满的情况,我们通常会浪费一个位置。也就是说,如果队列的总容量是100,那么实际上只能存储99个元素。这是因为我们需要保留一个位置来判断队列是满的还是空的。如果我们不这样做,那么在队列满和队列空时,front和rear指针都会指向同一个位置,......
  • FPGA设计之跨时钟域(CDC)设计篇(5)----同步FIFO的两种设计方法(计数器法/高位扩展法 | 手撕
    1、什么是FIFO?        FIFO(FirstInFirstOut)是一种先进先出的数据缓存器,在逻辑设计里面用的非常多。它是一种存储器结构,被广泛应用于芯片设计中。FIFO由存储单元队列或阵列构成,第一个被写入队列的数据也是第一个从队列中读出的数据。        FIFO设计可......
  • FPGA知识基础之--500ms计数器,边沿检测,按键消抖
    目录前言一、边沿检测1.1使用背景1.2方法:打拍法1.2.1背景1.2.2原理1.2.3上升沿二、计数器2.1原理2.2RTL代码三、按键消抖前言一、边沿检测1.1使用背景在我们设计电路时,经常会遇到需要继续检测上升沿和下降沿的电路,因此需要对边沿继续检测1.2方法:打......