Java CountDownLatch 原理、应用与最佳实践
Java CountDownLatch 是 Java 并发包中的同步辅助类,用于等待其他线程完成操作。深入解析其内部 AQS 实现机制,对比 CyclicBarrier 等工具,详解核心 API 如 await() 和 countDown()。涵盖复杂并发模式、生产环境最佳实践、常见问题陷阱及调试技巧,并提供电商订单处理系统实战示例,帮助开发者掌握高效线程协调方案。

Java CountDownLatch 是 Java 并发包中的同步辅助类,用于等待其他线程完成操作。深入解析其内部 AQS 实现机制,对比 CyclicBarrier 等工具,详解核心 API 如 await() 和 countDown()。涵盖复杂并发模式、生产环境最佳实践、常见问题陷阱及调试技巧,并提供电商订单处理系统实战示例,帮助开发者掌握高效线程协调方案。

CountDownLatch 是 Java 并发包(java.util.concurrent)中的一个同步辅助类,它允许一个或多个线程等待其他线程完成操作后再继续执行。可以将其理解为一个倒计时门闩,当计数器减到零时,等待的线程才会被释放。
import java.util.concurrent.CountDownLatch;
/**
* CountDownLatch 基本结构示例
*
* 核心特性:
* 1. 一次性使用:计数器归零后无法重置
* 2. 不可逆性:计数器只能递减,不能递增
* 3. 线程安全:内部使用 AQS 实现,保证线程安全
* 4. 等待机制:支持多个线程同时等待
*/
public class CountDownLatchStructure {
// CountDownLatch 的内部状态
static class LatchState {
private int count; // 计数器
public LatchState(int count) {
if (count < 0) {
throw new IllegalArgumentException("count < 0");
}
this.count = count;
}
public void countDown() {
synchronized (this) {
if (count > 0) {
count--;
}
}
}
public boolean await() throws InterruptedException {
synchronized (this) {
while (count > 0) {
this.wait(); // 等待计数器归零
}
return true;
}
}
}
public static void main(String[] args) {
// 创建 CountDownLatch,初始化计数器为 3
CountDownLatch latch = new CountDownLatch(3);
System.out.println("CountDownLatch 创建完成,初始计数器 = " + 3);
System.out.println("等待线程将阻塞,直到计数器归零");
}
}
| 特性 | CountDownLatch | CyclicBarrier | Semaphore | Phaser |
|---|---|---|---|---|
| 核心功能 | 等待其他线程完成 | 线程互相等待 | 控制并发数 | 更灵活的同步 |
| 重用性 | 一次性 | 可重用 | 可重用 | 可重用 |
| 计数器方向 | 递减 | 递增 | 增减 | 复杂状态 |
| 线程角色 | 主从模式 | 对等模式 | 资源控制 | 多阶段 |
| 典型场景 | 启动准备、任务分片 | 并行计算 | 连接池、限流 | 多阶段任务 |
并发同步工具选择逻辑:
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
/**
* CountDownLatch 内部 AQS 实现解析
*
* CountDownLatch 内部使用 AbstractQueuedSynchronizer (AQS) 实现,
* 这是一个基于 CLH 队列锁的框架。
*/
public class CountDownLatchInternal {
// 简化版 AQS 实现,展示 CountDownLatch 核心逻辑
static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count); // 使用 AQS 的 state 存储计数器
}
int getCount() {
return getState();
}
/**
* 尝试获取共享锁
* 当 state == 0 时返回 1,表示获取成功
* 否则返回 -1,表示需要加入等待队列
*/
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
/**
* 尝试释放共享锁(countDown 操作)
* 通过 CAS 递减 state,当 state 减到 0 时返回 true
*/
protected boolean tryReleaseShared(int releases) {
// 自旋 CAS 操作
for (; ; ) {
int c = getState();
(c == ) ;
c - ;
(compareAndSetState(c, nextc)) nextc == ;
}
}
}
InterruptedException {
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
;
(THREAD_COUNT);
System.out.println();
System.out.println( + THREAD_COUNT);
( ; i < THREAD_COUNT; i++) {
i;
(() -> {
{
System.out.println( + threadId + );
Thread.sleep();
System.out.println( + threadId + );
latch.countDown();
System.out.println( + threadId + + (THREAD_COUNT - threadId - ));
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
System.out.println();
latch.await();
System.out.println();
}
}
/**
* CountDownLatch 构造函数详解
*/
public class ConstructorDemo {
public static void main(String[] args) {
// 示例 1: 基本用法
System.out.println("=== 构造函数示例 ===");
// 创建 CountDownLatch,指定初始计数值
int initialCount = 5;
CountDownLatch latch1 = new CountDownLatch(initialCount);
System.out.println("创建 CountDownLatch,初始计数:" + initialCount);
// 示例 2: 边界条件测试
testBoundaryConditions();
// 示例 3: 实际应用场景
demonstrateRealWorldUsage();
}
private static void testBoundaryConditions() {
System.out.println("\n=== 边界条件测试 ===");
try {
// 测试 1: 计数为 0
CountDownLatch zeroLatch = new CountDownLatch(0);
System.out.println("创建计数为 0 的 latch");
zeroLatch.await(); // 应该立即返回
System.out.println("计数为 0 的 latch await() 立即返回");
// 测试 2: 计数为负数(应该抛出异常)
try {
CountDownLatch negativeLatch = new (-);
System.out.println();
} (IllegalArgumentException e) {
System.out.println( + e.getMessage());
}
;
(largeCount);
System.out.println( + largeCount);
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
{
System.out.println();
String[] modules = {, , , , };
(modules.length);
System.out.println( + modules.length + );
( ; i < modules.length; i++) {
modules[i];
(() -> {
{
() (Math.random() * ) + ;
Thread.sleep(loadTime);
System.out.println( + moduleName + + loadTime + );
moduleLatch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println( + moduleName + );
moduleLatch.countDown();
}
}, + moduleName).start();
}
{
System.out.println();
System.currentTimeMillis();
moduleLatch.await();
System.currentTimeMillis();
System.out.println();
System.out.println( + (endWait - startWait) + );
System.out.println();
} (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println();
}
}
}
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* await() 方法深度解析
*
* CountDownLatch 提供了多种 await 方法:
* 1. await(): 无限期等待
* 2. await(long timeout, TimeUnit unit): 超时等待
* 3. 组合使用模式
*/
public class AwaitMethodsDemo {
public static void main(String[] args) {
System.out.println("=== await() 方法详解 ===");
// 演示 1: 基本 await()
demonstrateBasicAwait();
// 演示 2: 超时 await()
demonstrateTimeoutAwait();
// 演示 3: 多个线程同时 await()
demonstrateMultipleAwait();
// 演示 4: 中断处理
demonstrateInterruption();
}
private static void demonstrateBasicAwait() {
System.out.println("\n--- 演示 1: 基本 await() ---");
CountDownLatch latch = new CountDownLatch(2);
new Thread(() -> {
try {
Thread.sleep(1000);
System.out.println("线程 A 完成任务,countDown");
latch.countDown();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
new Thread(() -> {
try {
Thread.sleep(1500);
System.out.println("线程 B 完成任务,countDown");
latch.countDown();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
{
System.out.println();
System.currentTimeMillis();
latch.await();
System.currentTimeMillis();
System.out.println( + (end - start) + );
System.out.println();
} (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println();
}
}
{
System.out.println();
();
( ; i <= ; i++) {
i;
(() -> {
{
Thread.sleep(id * );
System.out.println( + id + );
latch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
{
System.out.println();
System.currentTimeMillis();
latch.await(, TimeUnit.MILLISECONDS);
System.currentTimeMillis();
(completed) {
System.out.println();
} {
System.out.println( + latch.getCount() + );
}
System.out.println( + (end - start) + );
System.out.println();
} (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println();
}
}
{
System.out.println();
;
;
();
(TASK_COUNT);
();
( ; i < WAITER_COUNT; i++) {
i;
(() -> {
{
waitersReady.incrementAndGet();
System.out.println( + waiterId + );
startLatch.await();
System.out.println( + waiterId + );
Thread.sleep();
completionLatch.countDown();
System.out.println( + waiterId + );
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
(waitersReady.get() < WAITER_COUNT) {
Thread.();
}
{
Thread.sleep();
System.out.println( + WAITER_COUNT + );
System.out.println();
startLatch.countDown();
System.out.println();
completionLatch.await();
System.out.println( + WAITER_COUNT + );
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
{
System.out.println();
();
(() -> {
{
System.out.println();
latch.await();
System.out.println();
} (InterruptedException e) {
System.out.println( + Thread.currentThread().isInterrupted());
Thread.currentThread().interrupt();
}
});
waitingThread.start();
{
Thread.sleep();
System.out.println();
waitingThread.interrupt();
waitingThread.join();
System.out.println();
System.out.println( + latch.getCount());
System.out.println();
latch.countDown();
System.out.println( + latch.getCount());
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
import java.util.concurrent.atomic.AtomicLong;
/**
* countDown() 方法深度解析
*
* 关键特性:
* 1. 线程安全:使用 CAS 操作保证线程安全
* 2. 幂等性:计数为 0 后多次调用无效果
* 3. 无阻塞:立即返回,不会阻塞调用线程
*/
public class CountDownMethodDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("=== countDown() 方法详解 ===");
// 演示 1: 基本 countDown()
demonstrateBasicCountDown();
// 演示 2: 多线程并发 countDown()
demonstrateConcurrentCountDown();
// 演示 3: 计数为 0 后的行为
demonstrateZeroCountBehavior();
// 演示 4: countDown() 的性能特性
demonstratePerformance();
}
private static void demonstrateBasicCountDown() {
System.out.println("\n--- 演示 1: 基本 countDown() ---");
CountDownLatch latch = new CountDownLatch(3);
System.out.println("初始状态:");
System.out.println(" 计数器:" + latch.getCount());
System.out.println(" 是否归零:" + (latch.getCount() == 0));
System.out.println("\n执行 countDown() 1:");
latch.countDown();
System.out.println(" 计数器:" + latch.getCount());
System.out.println("执行 countDown() 2:");
latch.countDown();
System.out.println(" 计数器:" + latch.getCount());
System.out.println("执行 countDown() 3:");
latch.countDown();
System.out.println(" 计数器:" + latch.getCount());
System.out.println( + (latch.getCount() == ));
System.out.println();
{
latch.await();
System.out.println();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
InterruptedException {
System.out.println();
;
THREAD_COUNT;
(INITIAL_COUNT);
();
System.out.println( + THREAD_COUNT + );
System.out.println( + latch.getCount());
Thread[] threads = [THREAD_COUNT];
( ; i < THREAD_COUNT; i++) {
i;
threads[i] = (() -> {
{
Thread.sleep(() (Math.random() * ));
latch.getCount();
latch.countDown();
latch.getCount();
completedThreads.incrementAndGet();
System.out.printf(, threadId, before, after);
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
threads[i].start();
}
(Thread thread : threads) {
thread.join();
}
System.out.println();
System.out.println( + completedThreads.get());
System.out.println( + latch.getCount());
System.out.println( + (latch.getCount() == ));
(completedThreads.get() == THREAD_COUNT && latch.getCount() == ) {
System.out.println();
} {
System.out.println();
}
}
{
System.out.println();
();
System.out.println( + latch.getCount());
System.out.println();
latch.countDown();
System.out.println( + latch.getCount());
System.out.println();
latch.countDown();
System.out.println( + latch.getCount());
System.out.println();
latch.countDown();
System.out.println( + latch.getCount());
System.out.println();
System.out.println();
{
System.out.println();
System.currentTimeMillis();
latch.await();
System.currentTimeMillis();
System.out.println( + (end - start) + );
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
{
System.out.println();
;
;
System.out.println( + ITERATIONS + );
System.out.println();
(LATCH_COUNT);
System.currentTimeMillis();
( ; i < ITERATIONS; i++) {
latch1.countDown();
}
System.currentTimeMillis();
System.out.println( + (end1 - start1) + );
System.out.println( + (() (end1 - start1) / ITERATIONS * ) + );
System.out.println();
;
ITERATIONS / THREAD_COUNT;
(ITERATIONS);
();
(THREAD_COUNT);
Thread[] threads = [THREAD_COUNT];
System.currentTimeMillis();
( ; t < THREAD_COUNT; t++) {
t;
threads[t] = (() -> {
{
startLatch.await();
( ; i < COUNT_PER_THREAD; i++) {
latch2.countDown();
}
finishLatch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
threads[t].start();
}
startLatch.countDown();
{
finishLatch.await();
System.currentTimeMillis();
System.out.println( + (end2 - start2) + );
System.out.println( + (() (end2 - start2) / ITERATIONS * ) + );
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println();
System.out.println();
System.out.println();
System.out.println();
}
}
/**
* getCount() 方法详解
*
* getCount() 返回当前的计数值,常用于调试和状态监控。
* 注意:getCount() 的返回值只是一个快照,可能立即过时。
*/
public class GetCountMethodDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("=== getCount() 方法详解 ===");
// 演示 1: 基本使用
demonstrateBasicUsage();
// 演示 2: 监控和调试
demonstrateMonitoring();
// 演示 3: 竞态条件演示
demonstrateRaceCondition();
// 演示 4: 实际应用场景
demonstrateRealWorldUsage();
}
private static void demonstrateBasicUsage() {
System.out.println("\n--- 演示 1: 基本使用 ---");
CountDownLatch latch = new CountDownLatch(5);
System.out.println("初始状态:");
System.out.println(" getCount() = " + latch.getCount());
System.out.println("\n执行 countDown() 后:");
latch.countDown();
System.out.println(" getCount() = " + latch.getCount());
latch.countDown();
System.out.println(" getCount() = " + latch.getCount());
latch.countDown();
latch.countDown();
latch.countDown();
System.out.println(" 执行 3 次 countDown() 后:");
System.out.println(" getCount() = " + latch.getCount());
System.out.println("\n重要提示:");
System.out.println("1. getCount() 返回的是调用瞬间的快照值");
System.out.println("2. 在多线程环境中,返回值可能立即过时");
System.out.println("3. 不要基于 getCount() 的返回值做业务决策");
}
InterruptedException {
System.out.println();
;
(TASK_COUNT);
System.out.println( + TASK_COUNT + );
System.out.println( + latch.getCount());
(() -> {
{
(latch.getCount() > ) {
System.out.printf(, latch.getCount());
Thread.sleep();
}
System.out.println();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
monitorThread.setDaemon();
monitorThread.start();
( ; i < TASK_COUNT; i++) {
i;
(() -> {
{
() (Math.random() * ) + ;
Thread.sleep(duration);
latch.countDown();
System.out.printf(, taskId, duration);
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
latch.await();
System.out.println();
Thread.sleep();
}
InterruptedException {
System.out.println();
;
(THREAD_COUNT);
System.out.println();
System.out.println( + THREAD_COUNT + );
Thread[] threads = [THREAD_COUNT];
( ; i < THREAD_COUNT; i++) {
i;
threads[i] = (() -> {
latch.countDown();
});
}
(Thread thread : threads) {
thread.start();
}
System.out.println();
( ; i < ; i++) {
latch.getCount();
System.out.printf(, i + , count);
(count == ) {
System.out.println();
}
Thread.();
}
(Thread thread : threads) {
thread.join();
}
System.out.println( + latch.getCount());
System.out.println();
System.out.println();
System.out.println();
System.out.println();
}
{
System.out.println();
;
(BATCH_SIZE);
System.out.println( + BATCH_SIZE + );
System.out.println();
(() -> {
{
BATCH_SIZE;
() {
latch.getCount();
total - remaining;
() completed / total * ;
;
() (barWidth * completed / total);
System.out.printf( + barWidth + , .repeat(progress), percent, completed, total);
(remaining == ) {
System.out.println();
;
}
Thread.sleep();
}
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
( ; i < BATCH_SIZE; i++) {
i;
(() -> {
{
Thread.sleep(() (Math.random() * ));
latch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
latch.countDown();
}
}).start();
}
{
latch.await();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/**
* CountDownLatch 高级应用场景
*
* 1. 并行任务分片与聚合
* 2. 多阶段工作流
* 3. 资源初始化协调
* 4. 测试并发场景
*/
public class AdvancedUsageDemo {
/**
* 场景 1: 并行计算 Map-Reduce 模式
*/
static class ParallelMapReduce {
public static void main(String[] args) throws InterruptedException {
System.out.println("=== 场景 1: 并行 Map-Reduce ===");
// 模拟数据
List<Integer> data = new ArrayList<>();
for (int i = 1; i <= 100; i++) {
data.add(i);
}
// 将数据分成 4 个分片
int sliceCount = 4;
int sliceSize = data.size() / sliceCount;
CountDownLatch mapLatch = new CountDownLatch(sliceCount);
List<Future<Integer>> results = new ArrayList<>();
ExecutorService executor = Executors.newFixedThreadPool(sliceCount);
System.out.println("开始并行 Map 阶段...");
System.currentTimeMillis();
( ; i < sliceCount; i++) {
i;
i * sliceSize;
(i == sliceCount - ) ? data.size() : (i + ) * sliceSize;
Callable<Integer> task = () -> {
{
;
( fromIndex; j < toIndex; j++) {
sum += data.get(j);
Thread.sleep();
}
System.out.printf(, sliceIndex, sum);
sum;
} {
mapLatch.countDown();
}
};
results.add(executor.submit(task));
}
mapLatch.await();
System.out.println();
;
(Future<Integer> future : results) {
{
finalResult += future.get();
} (ExecutionException e) {
e.printStackTrace();
}
}
System.currentTimeMillis();
System.out.println( + finalResult);
System.out.println( + (endTime - startTime) + );
data.stream().mapToInt(Integer::intValue).sum();
System.out.println( + expected);
System.out.println( + (finalResult == expected ? : ));
executor.shutdown();
}
}
{
InterruptedException {
System.out.println();
();
();
();
System.out.println();
System.out.println();
( ; i <= ; i++) {
i;
(() -> {
{
System.out.printf(, taskId);
Thread.sleep( + taskId * );
System.out.printf(, taskId);
stage1Latch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
(() -> {
{
stage1Latch.await();
System.out.println();
();
( ; i <= ; i++) {
i;
(() -> {
{
System.out.printf(, taskId);
Thread.sleep( + taskId * );
System.out.printf(, taskId);
processLatch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
processLatch.await();
System.out.println();
stage2Latch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
(() -> {
{
stage2Latch.await();
System.out.println();
System.out.println();
Thread.sleep();
System.out.println();
stage3Latch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
stage3Latch.await();
System.out.println();
}
}
{
{
String name;
initTime;
;
{
.name = name;
.initTime = initTime;
}
{
(() -> {
{
System.out.printf(, name, initTime);
Thread.sleep(initTime);
initialized = ;
System.out.printf(, name);
latch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.printf(, name);
latch.countDown();
}
}).start();
}
{
initialized;
}
}
InterruptedException {
System.out.println();
Service[] services = {
(, ),
(, ),
(, ),
(, ),
(, )
};
(services.length);
System.out.println( + services.length + );
(Service service : services) {
service.initialize(initLatch);
}
(() -> {
{
(initLatch.getCount() > ) {
initLatch.getCount();
System.out.printf(, remaining);
Thread.sleep();
}
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
initLatch.await();
System.out.println();
System.out.println();
;
(Service service : services) {
(!service.isInitialized()) {
System.out.printf(, service.name);
allReady = ;
}
}
(allReady) {
System.out.println();
} {
System.out.println();
}
}
}
{
InterruptedException {
();
(nThreads);
( ; i < nThreads; i++) {
(() -> {
{
startGate.await();
{
task.run();
} {
endGate.countDown();
}
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
t.start();
}
System.nanoTime();
startGate.countDown();
endGate.await();
System.nanoTime();
end - start;
}
InterruptedException {
System.out.println();
() -> {
;
( ; i < ; i++) {
sum += i % ;
}
{
Thread.sleep();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
[] threadCounts = {, , , , , };
System.out.println();
System.out.println();
System.out.println();
;
( nThreads : threadCounts) {
timeTasks(nThreads, testTask);
(nThreads == ) {
singleThreadTime = duration;
}
() singleThreadTime / duration;
System.out.printf(, nThreads, duration, speedup);
}
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
}
}
}
import java.util.concurrent.atomic.AtomicBoolean;
/**
* CountDownLatch 生产环境最佳实践
*
* 包括错误处理、资源管理、性能优化等
*/
public class ProductionBestPractices {
/**
* 实践 1: 异常安全的使用模式
*/
static class ExceptionSafePattern {
public static void executeWithWorkers(int workerCount) throws InterruptedException {
CountDownLatch completionLatch = new CountDownLatch(workerCount);
AtomicBoolean hasError = new AtomicBoolean(false);
for (int i = 0; i < workerCount; i++) {
final int workerId = i;
new Thread(() -> {
try {
// 执行工作任务
doWork(workerId);
// 只有成功完成才减少计数
if (!hasError.get()) {
completionLatch.countDown();
System.out.printf("Worker %d completed successfully%n", workerId);
}
} catch (Exception e) {
// 发生异常,设置错误标志
hasError.set();
System.err.printf(, workerId, e.getMessage());
(completionLatch.getCount() > ) {
completionLatch.countDown();
}
}
}).start();
}
completionLatch.await();
(hasError.get()) {
System.out.println();
handleError();
} {
System.out.println();
}
}
Exception {
(Math.random() < ) {
();
}
Thread.sleep();
}
{
System.out.println();
}
}
{
{
CountDownLatch latch;
AtomicBoolean cancelled;
{
.latch = (taskCount);
.cancelled = ();
}
{
(() -> {
(cancelled.get()) {
System.out.println();
latch.countDown();
;
}
{
task.run();
System.out.println();
} (Exception e) {
System.err.println( + e.getMessage());
} {
latch.countDown();
}
}).start();
}
InterruptedException {
latch.await(timeout, unit);
}
{
cancelled.set();
(latch.getCount() > ) {
latch.countDown();
}
}
}
{
();
( ; i < ; i++) {
i;
executor.executeTask(() -> {
{
Thread.sleep();
System.out.println( + taskId + );
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
(() -> {
{
Thread.sleep();
System.out.println();
executor.cancelAll();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
{
executor.awaitCompletion(, TimeUnit.SECONDS);
(completed) {
System.out.println();
} {
System.out.println();
}
} (InterruptedException e) {
System.out.println();
executor.cancelAll();
}
}
}
{
{
CountDownLatch shutdownLatch;
List<AutoCloseable> resources;
{
.shutdownLatch = ();
.resources = <>();
}
{
resources.add(resource);
}
{
System.out.println();
(resources.size());
(AutoCloseable resource : resources) {
(() -> {
{
resource.close();
System.out.println( + resource.getClass().getSimpleName());
} (Exception e) {
System.err.println( + e.getMessage());
} {
cleanupLatch.countDown();
}
}).start();
}
{
cleanupLatch.await(, TimeUnit.SECONDS);
System.out.println();
} (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println();
} {
shutdownLatch.countDown();
}
}
InterruptedException {
shutdownLatch.await();
}
}
Exception {
();
manager.addResource(() -> {
System.out.println();
Thread.sleep();
});
manager.addResource(() -> {
System.out.println();
Thread.sleep();
});
manager.addResource(() -> {
System.out.println();
Thread.sleep();
});
(manager::shutdown).start();
manager.awaitShutdown();
System.out.println();
}
}
{
{
String name;
creationTime;
lastCountDownTime;
();
{
(count);
.name = name;
.creationTime = System.currentTimeMillis();
.lastCountDownTime = creationTime;
}
{
.countDown();
countDownCount.incrementAndGet();
lastCountDownTime = System.currentTimeMillis();
}
{
getCount();
System.currentTimeMillis() - creationTime;
System.currentTimeMillis() - lastCountDownTime;
System.out.println();
System.out.println( + name);
System.out.println( + countDownCount.get() + currentCount);
System.out.println( + currentCount);
System.out.println( + countDownCount.get());
System.out.println( + elapsed + );
System.out.println( + timeSinceLastCountDown + );
(currentCount == ) {
System.out.println();
} (timeSinceLastCountDown > ) {
System.out.println();
} {
System.out.println();
}
}
}
InterruptedException {
(, );
( ; i < ; i++) {
i;
(() -> {
{
Thread.sleep( + taskId * );
latch.countDown();
System.out.println( + taskId + );
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
(() -> {
(latch.getCount() > ) {
{
Thread.sleep();
latch.printStats();
} (InterruptedException e) {
Thread.currentThread().interrupt();
;
}
}
}).start();
latch.await();
System.out.println();
latch.printStats();
}
}
}
import java.util.concurrent.atomic.AtomicInteger;
/**
* CountDownLatch 常见错误与解决方案
*/
public class CommonPitfallsAndSolutions {
/**
* 陷阱 1: 忘记调用 countDown()
*/
static class Pitfall1_ForgetCountDown {
public static void wrongWay() throws InterruptedException {
System.out.println("=== 陷阱 1: 忘记调用 countDown() ===");
CountDownLatch latch = new CountDownLatch(3);
// 只启动两个线程,但 latch 需要三个 countDown
for (int i = 0; i < 2; i++) {
new Thread(() -> {
try {
Thread.sleep(1000);
latch.countDown(); // 这里只调用两次
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
System.out.println("主线程等待...");
latch.await(); // 这里会永远等待!
System.out.println("这行永远不会执行");
}
public static void correctWay() throws InterruptedException {
System.out.println();
();
();
( ; i < ; i++) {
(() -> {
{
(Math.random() < ) {
();
}
Thread.sleep();
completedTasks.incrementAndGet();
} (Exception e) {
System.err.println( + e.getMessage());
} {
latch.countDown();
System.out.println( + latch.getCount());
}
}).start();
}
latch.await(, TimeUnit.SECONDS);
(completed) {
System.out.println( + completedTasks.get());
} {
System.out.println( + latch.getCount());
}
}
}
{
{
System.out.println();
();
(() -> {
{
System.out.println();
latch.await();
System.out.println();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
System.out.println();
}
InterruptedException {
System.out.println();
();
();
(() -> {
{
System.out.println();
startLatch.await();
System.out.println();
Thread.sleep();
System.out.println();
} (InterruptedException e) {
Thread.currentThread().interrupt();
} {
finishLatch.countDown();
}
});
worker.start();
Thread.sleep();
System.out.println();
startLatch.countDown();
System.out.println();
finishLatch.await();
System.out.println();
}
}
{
InterruptedException {
System.out.println();
();
(() -> {
{
Thread.sleep();
latch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
latch.await();
System.out.println();
System.out.println();
(() -> {
{
Thread.sleep();
latch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
latch.await();
System.out.println();
}
InterruptedException {
System.out.println();
System.out.println();
java.util.concurrent. .util.concurrent.CyclicBarrier();
( ; i < ; i++) {
i + ;
(() -> {
{
System.out.println( + round + );
Thread.sleep();
barrier.await();
System.out.println( + round + );
} (Exception e) {
e.printStackTrace();
}
}).start();
Thread.sleep();
}
Thread.sleep();
System.out.println();
( ; i < ; i++) {
();
i + ;
(() -> {
{
System.out.println( + round + );
Thread.sleep();
newLatch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
newLatch.await();
System.out.println( + round + );
}
}
}
{
InterruptedException {
System.out.println();
List<Runnable> tasks = <>();
( ; i < ; i++) {
i;
tasks.add(() -> {
{
Thread.sleep();
System.out.println( + taskId + );
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
();
(Runnable task : tasks) {
(() -> {
task.run();
latch.countDown();
}).start();
}
latch.await();
System.out.println();
}
InterruptedException {
System.out.println();
List<Runnable> tasks = <>();
( ; i < ; i++) {
i;
tasks.add(() -> {
{
Thread.sleep();
System.out.println( + taskId + );
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
(tasks.size());
(Runnable task : tasks) {
(() -> {
{
task.run();
} {
latch.countDown();
}
}).start();
}
latch.await(, TimeUnit.SECONDS);
(completed) {
System.out.println();
} {
System.out.println( + latch.getCount());
}
System.out.println();
Executors.newFixedThreadPool();
List<Future<?>> futures = <>();
(Runnable task : tasks) {
futures.add(executor.submit(task));
}
(Future<?> future : futures) {
{
future.get();
} (ExecutionException e) {
System.err.println( + e.getCause().getMessage());
}
}
System.out.println();
executor.shutdown();
}
}
InterruptedException {
Pitfall1_ForgetCountDown.correctWay();
Pitfall2_AwaitBeforeCountDown.correctWay();
Pitfall3_ReuseAttempt.correctWay();
Pitfall4_CountMismatch.correctWay();
System.out.println();
}
}
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.locks.LockSupport;
/**
* CountDownLatch 调试技巧与工具
*/
public class DebuggingTechniques {
/**
* 技巧 1: 线程转储分析
*/
static class ThreadDumpAnalyzer {
public static void analyzeDeadlock() throws InterruptedException {
System.out.println("=== 调试技巧 1: 线程转储分析 ===");
CountDownLatch latch = new CountDownLatch(1);
// 创建可能死锁的场景
Thread waitingThread = new Thread(() -> {
try {
System.out.println("等待线程:开始等待 latch");
latch.await(); // 这会阻塞
System.out.println("等待线程:被唤醒");
} catch (InterruptedException e) {
System.out.println("等待线程:被中断");
Thread.currentThread().interrupt();
}
}, "WaitingThread");
waitingThread.start();
// 给线程时间进入等待状态
Thread.sleep(100);
System.out.println("\n当前线程状态:");
printThreadDump();
// 模拟忘记调用 countDown
System.out.println();
System.out.println();
Thread.sleep();
System.out.println();
printThreadDump();
latch.countDown();
waitingThread.join();
}
{
ManagementFactory.getThreadMXBean();
[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
(deadlockedThreads != && deadlockedThreads.length > ) {
System.out.println();
( threadId : deadlockedThreads) {
threadMXBean.getThreadInfo(threadId);
System.out.println( + threadInfo.getThreadName());
}
} {
System.out.println();
}
System.out.println();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(, );
(ThreadInfo threadInfo : threadInfos) {
(threadInfo.getThreadName().contains() || threadInfo.getThreadName().equals()) {
System.out.printf(, threadInfo.getThreadName(), threadInfo.getThreadState());
}
}
}
}
{
String name;
StackTraceElement[] creationStackTrace;
List<StackTraceElement[]> awaitStackTraces = Collections.synchronizedList( <>());
List<Long> countDownTimestamps = Collections.synchronizedList( <>());
{
(count);
.name = name;
.creationStackTrace = Thread.currentThread().getStackTrace();
}
InterruptedException {
awaitStackTraces.add(Thread.currentThread().getStackTrace());
System.out.printf(, name, Thread.currentThread().getName());
System.currentTimeMillis();
.await();
System.currentTimeMillis();
System.out.printf(, name, Thread.currentThread().getName(), end - start);
}
InterruptedException {
awaitStackTraces.add(Thread.currentThread().getStackTrace());
System.out.printf(, name, Thread.currentThread().getName(), timeout, unit);
System.currentTimeMillis();
.await(timeout, unit);
System.currentTimeMillis();
System.out.printf(, name, Thread.currentThread().getName(), result, end - start);
result;
}
{
countDownTimestamps.add(System.currentTimeMillis());
System.out.printf(, name, getCount() - );
.countDown();
}
{
System.out.println( + name + );
System.out.println();
( ; i < Math.min(creationStackTrace.length, ); i++) {
System.out.println( + creationStackTrace[i]);
}
System.out.println( + awaitStackTraces.size() + );
( ; i < awaitStackTraces.size(); i++) {
System.out.println( + (i + ) + );
StackTraceElement[] trace = awaitStackTraces.get(i);
( ; j < Math.min(trace.length, ); j++) {
System.out.println( + trace[j]);
}
}
System.out.println( + countDownTimestamps.size() + );
(!countDownTimestamps.isEmpty()) {
countDownTimestamps.get();
( ; i < countDownTimestamps.size(); i++) {
countDownTimestamps.get(i);
System.out.printf(, i + , timestamp - first);
}
}
System.out.println( + getCount());
}
}
{
{
CountDownLatch latch;
String serviceName;
startTime;
lastActivityTime;
{
.serviceName = name;
.latch = (taskCount);
.startTime = System.currentTimeMillis();
.lastActivityTime = startTime;
}
{
latch.countDown();
lastActivityTime = System.currentTimeMillis();
}
InterruptedException {
latch.await();
}
String {
serviceName;
}
{
latch.getCount();
}
{
System.currentTimeMillis() - startTime;
}
{
System.currentTimeMillis() - lastActivityTime;
}
{
latch.getCount() == ;
}
}
{
String ;
;
;
;
;
}
Exception {
System.out.println();
(, );
System.out.println();
System.out.println( + service.getServiceName());
System.out.println( + service.getRemainingCount());
System.out.println( + service.getUptime() + );
( ; i < ; i++) {
i;
(() -> {
{
Thread.sleep( + taskId * );
service.countDown();
System.out.printf(, taskId, service.getRemainingCount());
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
(() -> {
(service.getRemainingCount() > ) {
{
Thread.sleep();
System.out.printf(, service.getRemainingCount(), service.getTimeSinceLastActivity());
} (InterruptedException e) {
Thread.currentThread().interrupt();
;
}
}
System.out.println();
}).start();
service.await();
System.out.println();
}
}
{
{
InterruptedException {
(threadCount);
();
( ; i < threadCount; i++) {
(() -> {
{
Thread.sleep();
completedCount.incrementAndGet();
} (InterruptedException e) {
Thread.currentThread().interrupt();
} {
latch.countDown();
}
}).start();
}
latch.await(timeout, unit);
(completed && completedCount.get() == threadCount) {
System.out.println();
;
} {
System.out.printf(, completed, completedCount.get(), threadCount);
;
}
}
InterruptedException {
();
( ; i < ; i++) {
(() -> {
{
Thread.sleep();
latch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
latch.await(, TimeUnit.MILLISECONDS);
(!completed && latch.getCount() == ) {
System.out.println();
;
} {
System.out.printf(, completed, latch.getCount());
;
}
}
InterruptedException {
();
();
(() -> {
{
latch.await();
} (InterruptedException e) {
wasInterrupted.set();
Thread.currentThread().interrupt();
}
});
waitingThread.start();
Thread.sleep();
waitingThread.interrupt();
waitingThread.join();
wasInterrupted.get() || waitingThread.isInterrupted();
(interrupted) {
System.out.println();
;
} {
System.out.println();
;
}
}
}
InterruptedException {
System.out.println();
System.out.println();
LatchTestUtils.testLatchFunctionality(, , TimeUnit.MILLISECONDS);
System.out.println();
LatchTestUtils.testTimeoutFunctionality();
System.out.println();
LatchTestUtils.testInterruptionHandling();
System.out.println();
System.out.println( + (test1 ? : ));
System.out.println( + (test2 ? : ));
System.out.println( + (test3 ? : ));
(test1 && test2 && test3) {
System.out.println();
} {
System.out.println();
}
}
}
Exception {
System.out.println();
System.out.println();
System.out.println();
System.out.println();
(, );
(() -> {
{
debugLatch.await();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, ).start();
(() -> {
{
Thread.sleep();
debugLatch.countDown();
Thread.sleep();
debugLatch.countDown();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
Thread.sleep();
debugLatch.printDebugInfo();
System.out.println();
UnitTestPatterns.main( []);
}
}
/**
* CountDownLatch 使用总结
*/
public class CountDownLatchSummary {
/**
* 适用场景
*/
static class UseCases {
// 1. 服务启动协调
// 2. 并行任务分片聚合
// 3. 测试并发场景
// 4. 多阶段工作流协调
// 5. 资源初始化同步
}
/**
* 最佳实践
*/
static class BestPractices {
// 1. 总是在 finally 块中调用 countDown()
// 2. 设置合理的超时时间
// 3. 根据实际任务数量动态设置计数
// 4. 考虑使用 ExecutorService 替代手动线程管理
// 5. 添加适当的监控和日志
}
/**
* 常见陷阱
*/
static class CommonPitfalls {
// 1. 忘记调用 countDown() - 导致永久等待
// 2. 计数不匹配 - 多计数或少计数
// 3. 尝试重用 - CountDownLatch 是一次性的
// 4. 没有处理中断 - await() 可能被中断
// 5. 依赖 getCount() 做业务逻辑 - 返回值是快照
}
/**
* 替代方案
*/
static class Alternatives {
// 1. CyclicBarrier - 可重用的屏障
// 2. Phaser - 更灵活的同步器
// 3. CompletableFuture - 更现代的异步编程
// 4. ExecutorService.invokeAll() - 批量任务执行
}
public static void main(String[] args) {
System.out.println("=== CountDownLatch 总结 ===");
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
}
}
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
/**
* 完整示例:电商订单处理系统
* 演示 CountDownLatch 在实际业务场景中的应用
*/
public class ECommerceOrderSystem {
/**
* 订单处理服务
*/
static class OrderProcessingService {
private final ExecutorService executor;
private final InventoryService inventoryService;
private final PaymentService paymentService;
private final ShippingService shippingService;
private final NotificationService notificationService;
public OrderProcessingService(int threadPoolSize) {
this.executor = Executors.newFixedThreadPool(threadPoolSize);
this.inventoryService = new InventoryService();
this.paymentService = new PaymentService();
this.shippingService = new ShippingService();
this.notificationService = new NotificationService();
}
/**
* 处理单个订单
*/
public CompletableFuture<OrderResult> processOrder(Order order) {
return CompletableFuture.supplyAsync(() -> {
System.out.printf(, order.getId());
();
();
();
executor.execute(() -> {
{
inventoryService.checkInventory(order.getProductId(), order.getQuantity());
inventoryAvailable.set(available);
} {
validationLatch.countDown();
}
});
executor.execute(() -> {
{
paymentService.validatePayment(order.getPaymentMethod(), order.getAmount());
paymentValid.set(valid);
} {
validationLatch.countDown();
}
});
{
(!validationLatch.await(, TimeUnit.SECONDS)) {
OrderResult.failed(order.getId(), );
}
(!inventoryAvailable.get()) {
OrderResult.failed(order.getId(), );
}
(!paymentValid.get()) {
OrderResult.failed(order.getId(), );
}
();
AtomicReference<String> inventoryResult = <>();
AtomicReference<String> paymentResult = <>();
AtomicReference<String> shippingResult = <>();
executor.execute(() -> {
{
inventoryService.deductInventory(order.getProductId(), order.getQuantity());
inventoryResult.set(result);
} {
processingLatch.countDown();
}
});
executor.execute(() -> {
{
paymentService.processPayment(order.getPaymentMethod(), order.getAmount());
paymentResult.set(result);
} {
processingLatch.countDown();
}
});
executor.execute(() -> {
{
shippingService.arrangeShipping(order.getShippingAddress());
shippingResult.set(result);
} {
processingLatch.countDown();
}
});
(!processingLatch.await(, TimeUnit.SECONDS)) {
OrderResult.failed(order.getId(), );
}
(inventoryResult.get() == || paymentResult.get() == || shippingResult.get() == ) {
OrderResult.failed(order.getId(), );
}
notificationService.sendOrderConfirmation(order);
OrderResult.success(order.getId(), Arrays.asList(inventoryResult.get(), paymentResult.get(), shippingResult.get()));
} (InterruptedException e) {
Thread.currentThread().interrupt();
OrderResult.failed(order.getId(), );
}
}, executor);
}
CompletableFuture<BatchResult> {
System.out.printf(, orders.size());
(orders.size());
();
();
List<OrderResult> results = Collections.synchronizedList( <>());
(Order order : orders) {
processOrder(order).whenComplete((result, error) -> {
(error != ) {
failureCount.incrementAndGet();
results.add(OrderResult.failed(order.getId(), error.getMessage()));
} (result.isSuccess()) {
successCount.incrementAndGet();
results.add(result);
} {
failureCount.incrementAndGet();
results.add(result);
}
batchLatch.countDown();
});
}
CompletableFuture.supplyAsync(() -> {
{
batchLatch.await(, TimeUnit.SECONDS);
(orders.size(), successCount.get(), failureCount.get(), results);
} (InterruptedException e) {
Thread.currentThread().interrupt();
BatchResult.failed();
}
});
}
{
executor.shutdown();
{
(!executor.awaitTermination(, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
{
{
Math.random() > ;
}
String {
{
Thread.sleep(() (Math.random() * ));
;
} (InterruptedException e) {
Thread.currentThread().interrupt();
;
}
}
}
{
{
Math.random() > ;
}
String {
{
Thread.sleep(() (Math.random() * ));
String.format(, amount, method);
} (InterruptedException e) {
Thread.currentThread().interrupt();
;
}
}
}
{
String {
{
Thread.sleep(() (Math.random() * ));
+ address;
} (InterruptedException e) {
Thread.currentThread().interrupt();
;
}
}
}
{
{
CompletableFuture.runAsync(() -> {
{
Thread.sleep();
System.out.printf(, order.getId());
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
}
{
String id;
String productId;
quantity;
String paymentMethod;
amount;
String shippingAddress;
{
.id = id;
.productId = productId;
.quantity = quantity;
.paymentMethod = paymentMethod;
.amount = amount;
.shippingAddress = shippingAddress;
}
String {
id;
}
String {
productId;
}
{
quantity;
}
String {
paymentMethod;
}
{
amount;
}
String {
shippingAddress;
}
}
{
String orderId;
success;
String message;
List<String> details;
{
.orderId = orderId;
.success = success;
.message = message;
.details = details;
}
OrderResult {
(orderId, , , details);
}
OrderResult {
(orderId, , message, );
}
{
success;
}
String {
orderId;
}
String {
message;
}
List<String> {
details;
}
}
{
total;
success;
failure;
List<OrderResult> details;
String error;
{
.total = total;
.success = success;
.failure = failure;
.details = details;
.error = ;
}
BatchResult {
(, , , , error);
}
{
.total = total;
.success = success;
.failure = failure;
.details = details;
.error = error;
}
{
total;
}
{
success;
}
{
failure;
}
{
total > ? () success / total * : ;
}
}
Exception {
System.out.println();
System.out.println();
();
List<Order> orders = <>();
( ; i <= ; i++) {
orders.add( ( + String.format(, i), + (i % + ), (i % + ) * , i % == ? : , * (i % + ), + i));
}
System.out.printf(, orders.size());
System.currentTimeMillis();
service.processBatch(orders).get();
System.currentTimeMillis();
System.out.println();
System.out.printf(, result.getTotal());
System.out.printf(, result.getSuccess());
System.out.printf(, result.getFailure());
System.out.printf(, result.getSuccessRate());
System.out.printf(, endTime - startTime);
System.out.println();
(result.getDetails() != ) {
;
(OrderResult orderResult : result.getDetails()) {
(count++ < ) {
System.out.printf(, orderResult.getOrderId(), orderResult.getMessage());
}
}
(result.getDetails().size() > ) {
System.out.printf(, result.getDetails().size() - );
}
}
service.shutdown();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
}
}
CountDownLatch 是 Java 并发编程中的基础且强大的工具,它的核心价值在于:
关键要点回顾:
进阶方向:
掌握 CountDownLatch 不仅是学习一个工具,更是理解并发编程思想的重要一步。它在简单的 API 背后,体现了并发控制的核心原则:协调、同步和通信。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online