有人能帮助我理解什么是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();
}
}
// -----------------------------------------------------
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.");
}
}
CountDownLatch在Java中是一种同步器,它允许一个线程在开始处理之前等待一个或多个线程。
CountDownLatch工作在闩锁原理,线程将等待直到门打开。在创建CountDownLatch时,一个线程等待指定的n个线程。
例如:final CountDownLatch = new CountDownLatch(3);
这里我们将计数器设置为3。
任何调用CountDownLatch.await()的线程(通常是应用程序的主线程)都将等待,直到count为零或被另一个线程中断。所有其他线程都需要在完成或准备工作后调用CountDownLatch.countDown()来进行倒计时。一旦count达到0,等待的线程就开始运行。
这里的计数是通过CountDownLatch.countDown()方法递减的。
调用await()方法的线程将一直等待,直到初始计数为零。
为了使计数为零,其他线程需要调用countDown()方法。
一旦计数为零,调用await()方法的线程将恢复(开始执行)。
CountDownLatch的缺点是它不可重用:一旦计数为零,它就不再可用。
当我们想要等待多个线程完成其任务时,就会使用它。它类似于线程连接。
我们可以在哪里使用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");
}
}
以上代码的输出为:
分配给开发团队devB的任务
分配给开发团队devA的任务
任务由开发团队devB完成
由开发团队devA完成的任务
分配给QA团队的任务
QA团队完成的任务
这里await()方法等待countdownlatch标志变为0,而countDown()方法将countdownlatch标志减1。
JOIN的限制:
上面的例子也可以通过JOIN实现,但是JOIN不能在两种情况下使用:
当我们使用ExecutorService而不是Thread类创建线程时。
修改上面的例子,经理希望在开发完成80%的任务后立即将代码移交给QA团队。这意味着CountDownLatch允许我们修改实现,可以用来等待另一个线程的部分执行。
NikolaB解释得很好,但是举例有助于理解,所以这里有一个简单的例子…
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....");
}
}