jjzjj

CountDownLatch

程序员札记 2023-03-28 原文

CountDownLatch是基于AQS的阻塞工具,阻塞一个或者多个线程,直到所有的线程都执行完成。

image.png

CountDownLatch解决了什么问题

当一个任务运算量比较大的时候,需要拆分为各种子任务,必须要所有子任务完成后才能汇总为总任务。
使用并发模拟的时候可以使用CountDownLatch.也可以设置超时等待时间,

CountDownLatch 用法

package com.conrrentcy.juc;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CountDownLatchExample {
    private static final Logger log = LoggerFactory.getLogger(CountDownLatchExample.class);
     //线程数量
    private static final int THREAD_NUM = 10;

    // CountdownLatch阻塞模拟
    public static void main(String[] args) throws InterruptedException {

        // 创建线程池 用于执行线程
        
        ExecutorService executorService = Executors.newCachedThreadPool();
        //创建countDownLatch
        final CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
        long startTime = System.currentTimeMillis();
        //循环创建线程
        for (int i = 0; i < THREAD_NUM; i++) {
            final int a = i;

            executorService.execute(() -> {
                try {
                    test(a);
                } catch (Exception e) {
                    log.error("Exception", e);
                } finally {
                    countDownLatch.countDown();
                }
            });

        }

        countDownLatch.await();
        long endTime = System.currentTimeMillis();
        log.info("执行完毕,{}-{}",startTime,endTime);
        executorService.shutdown();
    }

    private static void test(int num) throws InterruptedException {
        Thread.sleep(100);
        log.info("{}-{}", num,System.currentTimeMillis());
        Thread.sleep(100);
    }

}

阻塞所有线程执行完成后再执行

image.png

CountDownLatch源码解析

CountDownLatch源码中的方法和属性并不多,下面我们来一一解析。

1.AQS框架以及构造方法

//当前对象中私有阻塞工具

private final Sync sync;
// 模板方法模式重写AQS工具
private static final class Sync extends AbstractQueuedSynchronizer {
   private static final long serialVersionUID = 4982264981922014374L;
   // 共享阻塞AQS
   Sync(int count) {
       setState(count);
   }
   // 获取当前还剩多少资源可以使用
   int getCount() {
       return getState();
   }

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

   protected boolean tryReleaseShared(int releases) {
       for (;;) {
           int c = getState();
           if (c == 0)
               return false;
           int nextc = c-1;
           if (compareAndSetState(c, nextc))
               return nextc == 0;
       }
   }
}
//构造方法创建一个锁对象
public CountDownLatch(int count) {
   if (count < 0) throw new IllegalArgumentException("count < 0");
   this.sync = new Sync(count);
}

2. countDown()方法解析

该方法用于线程执行完毕后减计统计数量,

// 该方法时释放一个共享锁。当所有锁都被释放完成后主线程就能继续执行了。
public void countDown() {
    sync.releaseShared(1);
}

3.await()方法解析

//拦截主线程的方法。主线程在这里等待条件达成后继续执行。
public void await() throws InterruptedException {
    //在这里阻塞线程的执行
    sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
       throws InterruptedException {
   if (Thread.interrupted())
       throw new InterruptedException();
    //这里判断是否还有可以共享的资源
   // 如果有则返回-1 否则返回 1,重写AQS的方法参见(1.AQS框架以及构造方法)
   if (tryAcquireShared(arg) < 0)
       // 有资源则运行阻塞自旋等待所有线程执行完毕
       doAcquireSharedInterruptibly(arg);
   // 无资源可用就让线程继续执行
}

// 带延迟的减少数据拦截方法
// 返回的结果是没有跑完全部线程就继续执行下一步了。
public boolean await(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
    throws InterruptedException {
    //线程如果被中断则抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    // 表示如果线程被执行完了直接返回成功,如果没有执行完则看等待时间来决定是否要继续执行。
    return tryAcquireShared(arg) >= 0 ||
        doAcquireSharedNanos(arg, nanosTimeout);
}

再看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) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

下面是具体的流程


image.png

CountDownLatch 总结

CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。 在分散计算统一合成结果,按某个流程加载资源的方面有着非诚好用的效果。CountDownLatch是不能够重用的,如果需要重新计数,可以考虑使用CyclicBarrier或者创建新的CountDownLatch实例
下一篇我们讲解像蓄水池一样功能的Semphore。

有关CountDownLatch的更多相关文章

  1. java - 等待通知和 CountDownLatch 之间的区别 - 2

    我需要一些帮助来了解使用CountDownLatch相对于传统等待通知的优势。我认为notifyAll()确实做了同样的事情,而且似乎更容易使用(可能是因为熟悉)。另外,CountDownLatch的wait()和await()有什么区别?谢谢!编辑:我想我需要重新表述我的查询:Await()按照文档说:Causesthecurrentthreadtowaituntilthelatchhascounteddowntozero,unlessthethreadisinterrupted.对我来说,很难看出wait()和await()之间的区别-await()确实在幕后使用wait(),并且

  2. java - 为什么 CountDownLatch.getCount() 返回一个 long 而不是一个 int? - 2

    我查看了代码,一切都是int--传递给CountDownLatch构造函数的参数是int,Sync中的变量是int,Sync.getCount()的返回类型是int。但是CountDownLatch.getCount()返回一个长?想知道为什么。 最佳答案 除非设计该API的人回答,否则我不知道您是否会找到该问题的充分答案,但它确实说它用于“调试和测试”。publiclonggetCount(){...}//justfordebuggingandtesting 关于java-为什么Cou

  3. Java:是 CountDownLatch 线程安全的 - 2

    在docs对于CountDownLatch,我看到类似的内容:publicvoidrun(){try{startSignal.await();doWork();doneSignal.countDown();}catch(InterruptedExceptionex){}//return;}这里的startSignal和doneSignal是CountDownLatch对象。文档没有提及该类是否是线程安全的。 最佳答案 由于它被设计为由多个线程使用,因此可以公平地假设它是线程安全的线程安全的大多数含义。甚至还有一个happens-be

  4. java - CountDownLatch 的 latch.await() 方法与 Thread.join() - 2

    我看到一个stackoverflow成员建议使用Thread.join()让一个“主”线程等待2个“任务”线程完成。我会经常做一些不同的事情(如下所示),我想知道我的方法是否有任何问题。finalCountDownLatchlatch=newCountDownLatch(myItems.length);for(Itemitem:myItems){//doStufflaunchesaThreadthatcallslatch.countDown()asit'sfinalactitem.doStuff(latch);}latch.await();//ignoringExceptionsfor

  5. java - 超时后中止 countDownLatch.await() - 2

    我正在使用ExecutorService来实现一个3线程池,并使用CountDownLatch来监视所有线程的完成,以进行进一步处理。ExecutorServicethreadExecutor=Executors.newFixedThreadPool(3);CountDownLatchcountDownLatch=newCountDownLatch(3);AuthorisationHistoryTasktask1=newAuthorisationHistoryTask(commonDataThread,countDownLatch);PreAuthHistoryTasktask2=ne

  6. java - 多次调用 CountDownLatch.await(int) 超时 - 2

    我使用CountDownLatch等待来自另一个组件(在不同线程中运行)的特定事件。以下方法符合我的软件的语义,但我不确定它是否按我预期的那样工作:mCountDownLatch.await(3000,TimeUnit.MILLISECONDS)otherComponent.aStaticVolatileVariable=true;mCountDownLatch.await(3500,TimeUnit.MILLISECONDS);...场景应该是这样的:我等了3秒,如果latch没有倒数到0,我就用那个变量通知其他组件,然后我最多等3.5秒。如果再次超时,那我就不管了,继续进行其他操作

  7. java - CyclicBarrier/CountDownLatch 和 Java 中的 join 有什么区别? - 2

    Java中的CyclicBarrier/CountDownLatch和join有什么区别?CyclicBarrier和CountDownLatch有什么优势?在我看来,只需使用join我们就可以等待线程完成其执行。 最佳答案 是的,“t.join()”使当前线程等待“t”线程完成,当一个线程正在等待其他线程时,我们可以准备一个线程链。但有时CountDownLatch/CyclicBarrier更方便。首先,CountDownLatch/CyclicBarrier不要求所有工作线程都应该完成。线程可以在应用程序运行时一直运行。他们只

  8. java - 我如何 "cancel"一个 CountDownLatch? - 2

    我有多个消费者线程使用await()等待大小为1的CountDownLatch。我有一个生产者线程,它在成功完成时调用countDown()。这在没有错误的情况下效果很好。但是,如果生产者检测到错误,我希望它能够向消费者线程发出错误信号。理想情况下,我可以让生产者调用类似abortCountDown()的东西,并让所有消费者收到InterruptedException或其他一些异常。我不想调用countDown(),因为这需要我所有的消费者线程在调用await()之后再进行一次额外的手动检查是否成功。我宁愿他们只收到一个他们已经知道如何处理的异常。我知道CountDownLatch中没

  9. java - join 和 CountDownLatch 有什么区别? - 2

    当等待其他线程完成时,我们可以使用join或CountdownLatch。使用这两种机制的优缺点是什么? 最佳答案 如果您自己处理线程,则只能使用Thread.join。大多数人选择不直接处理线程处理的细节,而是使用ExecutorService为他们处理。ExecutorService不会直接显示它们是如何执行任务的,因此您必须使用CountDownLatch:(假设您不想只是shutdown整个服务,也就是。)ExecutorServiceservice=Executors.newFixedThreadPool(5);final

  10. java - join 和 CountDownLatch 有什么区别? - 2

    当等待其他线程完成时,我们可以使用join或CountdownLatch。使用这两种机制的优缺点是什么? 最佳答案 如果您自己处理线程,则只能使用Thread.join。大多数人选择不直接处理线程处理的细节,而是使用ExecutorService为他们处理。ExecutorService不会直接显示它们是如何执行任务的,因此您必须使用CountDownLatch:(假设您不想只是shutdown整个服务,也就是。)ExecutorServiceservice=Executors.newFixedThreadPool(5);final

随机推荐