CountDownLatch如何在Java多线程中使用?

ama*_*mal 178 java multithreading countdownlatch countdown

有人可以帮我理解Java CountDownLatch是什么以及何时使用它?

我对这个程序的工作原理并不十分清楚.据我所知,所有三个线程立即启动,每个线程将在3000ms后调用CountDownLatch.倒数会逐一减少.在锁存器变为零之后,程序打印"已完成".也许我理解的方式不正确.

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

class Processor implements Runnable {
    private CountDownLatch latch;

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

    public void run() {
        System.out.println("Started.");

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        latch.countDown();
    }
}
Run Code Online (Sandbox Code Playgroud)

// ------------------------------------------------ -----

public class App {

    public static void main(String[] args) {

        CountDownLatch latch = new CountDownLatch(3); // coundown from 3 to 0

        ExecutorService executor = Executors.newFixedThreadPool(3); // 3 Threads in pool

        for(int i=0; i < 3; i++) {
            executor.submit(new Processor(latch)); // ref to latch. each time call new Processes latch will count down by 1
        }

        try {
            latch.await();  // wait until latch counted down to 0
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Completed.");
    }

}
Run Code Online (Sandbox Code Playgroud)

Nik*_*laB 189

是的,你理解正确. CountDownLatch在锁存原理工作,主线程将等待门打开.一个线程等待n个线程,在创建时指定CountDownLatch.

任何线程,通常是应用程序的主线程,调用CountDownLatch.await()将等到计数达到零或被另一个线程中断.所有其他线程都需要通过CountDownLatch.countDown()在完成或准备好后调用来倒计时.

一旦计数达到零,等待线程就会继续.其中一个缺点/优点CountDownLatch是它不可重复使用:一旦计数达到零,就不能再使用CountDownLatch了.

编辑:

使用CountDownLatch当一个线程(比如主线程),需要等待一个或多个线程来完成,才能继续处理.

使用的一个典型例子CountDownLatch在Java中是使用服务的体系结构,其中由多个线程提供多种服务,直至所有服务已成功启动应用程序无法启动处理的服务器端核心Java应用程序.

PS OP的问题有一个非常简单的例子,所以我没有包含一个.

  • 有关如何使用CountDownLatch的教程,请访问http://howtodoinjava.com/2013/07/18/when-to-use-countdownlatch-java-concurrency-example-tutorial/ (10认同)
  • 我认为不可重用性是一个优点:你确定没有人可以重置它,或增加计数. (3认同)
  • 很好的解释.但是我会稍微不同意一点:在Java中创建CountDownLatch时,一个线程等待指定n个线程.如果你需要这样的机制,那么使用`CyclicBarrier`是明智的.这两者之间的基本概念差异,如"Java并发实践"中给出的那样:"锁存器用于等待事件; 障碍是等待其他线程`.`cyclicBarrier.await()`进入阻塞状态. (3认同)
  • 谢谢你的回复。你能给我一个在哪里应用倒计时闩锁的例子吗? (2认同)

Vis*_*ote 42

CountDownLatchJava中的一种同步器允许在开始处理之前Thread 等待一个或多个Threads.

CountDownLatch在锁存原理上工作,线程将一直等到门打开.一个线程等待n创建时指定的线程数CountDownLatch.

例如 final CountDownLatch latch = new CountDownLatch(3);

这里我们将计数器设置为3.

任何线程,通常是应用程序的主线程,调用CountDownLatch.await()将等待计数达到零或被另一个线程中断Thread.所有其他线程都需要通过CountDownLatch.countDown()在完成或准备好工作后调用来倒计时.一旦计数达到零,Thread等待开始运行.

这里计数通过CountDownLatch.countDown()方法递减.

Thread它调用await()方法将等到初始计数到达零.

要使计数为零,其他线程需要调用该countDown()方法.一旦计数变为零,调用该await()方法的线程将恢复(开始执行).

缺点CountDownLatch是它不可重复使用:一旦计数变为零,它就不再可用了.


vik*_*ait 23

尼古拉B解释得非常好,但是例子有助于理解,所以这里有一个简单的例子......

 import java.util.concurrent.*;


  public class CountDownLatchExample {

  public static class ProcessThread implements Runnable {

    CountDownLatch latch;
    long workDuration;
    String name;

    public ProcessThread(String name, CountDownLatch latch, long duration){
        this.name= name;
        this.latch = latch;
        this.workDuration = duration;
    }


    public void run() {
        try {
            System.out.println(name +" Processing Something for "+ workDuration/1000 + " Seconds");
            Thread.sleep(workDuration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name+ "completed its works");
        //when task finished.. count down the latch count...

        // basically this is same as calling lock object notify(), and object here is latch
        latch.countDown();
    }
}


public static void main(String[] args) {
    // Parent thread creating a latch object
    CountDownLatch latch = new CountDownLatch(3);

    new Thread(new ProcessThread("Worker1",latch, 2000)).start(); // time in millis.. 2 secs
    new Thread(new ProcessThread("Worker2",latch, 6000)).start();//6 secs
    new Thread(new ProcessThread("Worker3",latch, 4000)).start();//4 secs


    System.out.println("waiting for Children processes to complete....");
    try {
        //current thread will get notified if all chidren's are done 
        // and thread will resume from wait() mode.
        latch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("All Process Completed....");

    System.out.println("Parent Thread Resuming work....");



     }
  }
Run Code Online (Sandbox Code Playgroud)


V J*_* Jo 20

当我们想要等待多个线程完成其任务时使用它.它类似于加入线程.

我们可以在哪里使用CountDownLatch

考虑一个我们有需求的场景,我们有三个线程"A","B"和"C",我们只想在"A"和"B"线程完成或部分完成其任务时启动线程"C".

它可以应用于现实世界的IT场景

考虑一种情况,管理者在开发团队(A和B)之间划分模块,并且他希望将其分配给QA团队,以便仅在两个团队完成任务时进行测试.

public class Manager {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(2);
        MyDevTeam teamDevA = new MyDevTeam(countDownLatch, "devA");
        MyDevTeam teamDevB = new MyDevTeam(countDownLatch, "devB");
        teamDevA.start();
        teamDevB.start();
        countDownLatch.await();
        MyQATeam qa = new MyQATeam();
        qa.start();
    }   
}

class MyDevTeam extends Thread {   
    CountDownLatch countDownLatch;
    public MyDevTeam (CountDownLatch countDownLatch, String name) {
        super(name);
        this.countDownLatch = countDownLatch;       
    }   
    @Override
    public void run() {
        System.out.println("Task assigned to development team " + Thread.currentThread().getName());
        try {
                Thread.sleep(2000);
        } catch (InterruptedException ex) {
                ex.printStackTrace();
        }
    System.out.println("Task finished by development team Thread.currentThread().getName());
            this.countDownLatch.countDown();
    }
}

class MyQATeam extends Thread {   
    @Override
    public void run() {
        System.out.println("Task assigned to QA team");
        try {
                Thread.sleep(2000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        System.out.println("Task finished by QA team");
    }
}
Run Code Online (Sandbox Code Playgroud)

上述代码的输出将是:

分配给开发团队devB的任务

分配给开发团队devA的任务

任务由开发团队devB完成

任务由开发团队devA完成

任务分配给QA团队

任务由QA团队完成

这里await()方法等待countdownlatch标志变为0,countDown()方法将countdownlatch标志减1.

JOIN的限制: 以上示例也可以通过JOIN实现,但JOIN不能在两种情况下使用:

  1. 当我们使用ExecutorService而不是Thread类来创建线程时.
  2. 修改以上示例,一旦开发人员完成80%的任务,Manager就会将代码切换到QA团队.这意味着CountDownLatch允许我们修改可用于等待另一个线程进行部分执行的实现.