java.util.concurrent.CountDownLatch
类是JDK提供的同步工具类, 其功能大致是允许一个线程或多个线程一直等待直至其他线程的操作完成后再执行;
1. CountDownLatch
类中的内部类 Sync
Sync内部类
2. CountDownLatch
类的构造方法
CountDownLatch类的构造方法
3. CountDownLatch
类的常用方法
CountDownLatch类的常用方法
4. CountDownLatch
类的应用
CountDownLatch
类有两种典型的用法:
- 将一个任务分配给若干个线程执行, 等待所有任务线程执行完成后, 再进行汇总处理操作;
- 应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行;
注意:
countDown()
方法并没有规定一个线程只能调用一次该方法, 当同一个线程多次调用countDown()
方法时, 每次都会使计数器减一(直至计数器的值为0); 另外,await()
方法也并没有规定只能有一个线程执行该方法, 如果多个线程同时执行await()
方法, 则这几个线程都将处于等待状态, 并且以共享模式享有同一个锁;
- 例子1
假设要打印1至100, 最后再输出"Ok", 1至100的打印顺序不要求统一, 只需保证"Ok"是在最后出现即可;
思路: 定义一个CountDownLatch
, 然后启动10个线程分别打印(n-1)X10+1至(n-1)X10+10之间的数, 主线程中调用await()
方法等待所有线程的执行完毕, 每个线程执行完毕后都调用countDown()
方法, 最后主线程打印"Ok";
package com.chapter02.example01;
import lombok.AllArgsConstructor;
import java.util.concurrent.CountDownLatch;
/**
* @author dimdark
*/
@AllArgsConstructor
public class Worker extends Thread {
/**
* 所有工作线程到达的时间点
*/
private CountDownLatch arriveSignal;
/**
* 所有工作线程开始工作的时间点
*/
private CountDownLatch startSignal;
/**
* 所有工作线程完成工作的时间点
*/
private CountDownLatch endSignal;
/**
* 表示从startIndex这个数起开始按顺序打印10个数
*/
private int startIndex;
@Override
public void run() {
boolean isOccurError = false;
arriveSignal.countDown();
try {
startSignal.await();
} catch(InterruptedException e) {
isOccurError = true;
System.err.println("工作线程" + Thread.currentThread().getName() + "出现异常, 无法工作!");
e.printStackTrace();
} finally {
if (isOccurError) {
endSignal.countDown();
}
}
for (int i = startIndex; i < startIndex + 10; ++i) {
System.out.println(i);
}
endSignal.countDown();
}
}
package com.chapter02.example01;
import java.util.concurrent.CountDownLatch;
/**
* @author dimdark
*/
public class CountDownLatchTest {
private static final int WORKER_NUMBER = 10;
public static void main(String[] args) {
CountDownLatch arriveSignal = new CountDownLatch(WORKER_NUMBER);
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch endSignal = new CountDownLatch(WORKER_NUMBER);
Worker[] workers = new Worker[WORKER_NUMBER];
int startIndex = 1;
for (Worker worker : workers) {
worker = new Worker(arriveSignal, startSignal, endSignal, startIndex);
worker.start();
startIndex += 10;
}
try {
arriveSignal.await();
} catch(InterruptedException e) {
System.err.println("主线程出现异常, 无法保证工作线程同时开始工作!");
e.printStackTrace();
} finally {
startSignal.countDown();
}
try {
endSignal.await();
} catch(InterruptedException e) {
System.err.println("主线程出现异常, 无法执行汇合工作线程的工作成果!");
e.printStackTrace();
}
System.out.println("Ok");
}
}
- 例子2
模拟一个应用程序启动类, 它开始启动3个线程, 这些线程将检查外部系统, 启动类(主线程)等待直至所有的服务均检查成功, 此时启动类恢复执行;
package com.chapter02.example02;
import com.sun.istack.internal.NotNull;
import lombok.Getter;
import java.util.concurrent.CountDownLatch;
/**
* @author dimdark
*/
public abstract class BaseHealthChecker implements Runnable {
private CountDownLatch latch;
/**
* 服务名称
*/
@Getter
private String serviceName;
/**
* 服务启动状态
*/
@Getter
private boolean serviceUp;
public BaseHealthChecker(@NotNull String serviceName, @NotNull CountDownLatch latch) {
this.latch = latch;
this.serviceName = serviceName;
this.serviceUp = false;
}
@Override
public void run() {
try {
verifyService();
serviceUp = true;
} catch(Exception e) {
serviceUp = false;
e.printStackTrace();
} finally {
latch.countDown();
}
}
/**
* 服务检查的具体逻辑在该方法处定义
*/
public abstract void verifyService() throws Exception;
}
package com.chapter02.example02;
import com.sun.istack.internal.NotNull;
import java.util.concurrent.CountDownLatch;
/**
* @author dimdark
*/
public class DatabaseHealthChecker extends BaseHealthChecker {
public DatabaseHealthChecker(@NotNull CountDownLatch latch) {
super("Database Service", latch);
}
@Override
public void verifyService() throws Exception {
System.out.println("Checking " + this.getServiceName());
Thread.sleep(1000);
System.out.println(this.getServiceName() + "is Up");
}
}
package com.chapter02.example02;
import com.sun.istack.internal.NotNull;
import java.util.concurrent.CountDownLatch;
/**
* @author dimdark
*/
public class NetworkHealthChecker extends BaseHealthChecker {
public NetworkHealthChecker(@NotNull CountDownLatch latch) {
super("Network Service", latch);
}
@Override
public void verifyService() throws Exception {
System.out.println("Checking " + this.getServiceName());
Thread.sleep(1000);
System.out.println(this.getServiceName() + "is Up");
}
}
package com.chapter02.example02;
import com.sun.istack.internal.NotNull;
import java.util.concurrent.CountDownLatch;
/**
* @author dimdark
*/
public class CacheHealthChecker extends BaseHealthChecker {
public CacheHealthChecker(@NotNull CountDownLatch latch) {
super("Cache Service", latch);
}
@Override
public void verifyService() throws Exception {
System.out.println("Checking " + this.getServiceName());
Thread.sleep(1000);
System.out.println(this.getServiceName() + "is Up");
}
}
package com.chapter02.example02;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* @author dimdark
*/
public class ApplicationStartUpUtil {
/**
* 所有需要检查的外部服务
*/
private List<BaseHealthChecker> services;
private CountDownLatch latch;
private ApplicationStartUpUtil(){
latch = new CountDownLatch(3);
services = new ArrayList<>();
services.add(new NetworkHealthChecker(latch));
services.add(new CacheHealthChecker(latch));
services.add(new DatabaseHealthChecker(latch));
}
private static final ApplicationStartUpUtil INSTANCE = new ApplicationStartUpUtil();
public static ApplicationStartUpUtil getInstance() {
return INSTANCE;
}
/**
* 检查所有外部服务是否成功, 若有任意一个失败的服务则返回false
* 当所有服务均检查成功则返回true
* @return boolean 外部服务检查成功的状态
*/
public static boolean checkExternalServices() throws Exception {
Executor executor = Executors.newFixedThreadPool(INSTANCE.services.size());
for (BaseHealthChecker service : INSTANCE.services) {
executor.execute(service);
}
INSTANCE.latch.await();
for (BaseHealthChecker service : INSTANCE.services) {
if (!service.isServiceUp()) {
return false;
}
}
return true;
}
}
package com.chapter02.example02;
/**
* @author dimdark
*/
public class Test {
public static void main(String[] args) {
boolean result = false;
try {
result = ApplicationStartUpUtil.checkExternalServices();
} catch(Exception e) {
e.printStackTrace();
}
System.out.println("External services validation completed! Result: " + result);
}
}