Java并发工具深度剖析与实战
前言
Java并发编程是构建高性能应用的关键技术。Java提供了丰富的并发工具类,包括CountDownLatch、CyclicBarrier、Semaphore、Exchanger等,这些工具能够帮助我们优雅地解决多线程协作问题。本文将深入探讨这些并发工具的原理、使用场景以及在实际生产环境和开源框架中的应用。
一、CountDownLatch - 倒计时门闩
1.1 基本概念
CountDownLatch是一个同步辅助类,允许一个或多个线程等待其他线程完成操作。它通过一个计数器来实现,计数器的初始值为线程的数量,每当一个线程完成任务后计数器值减1,当计数器值为0时,等待的线程会被唤醒。
ini
初始状态: count = 3
+--------+ +--------+ +--------+ +--------+
| 主线程 | | 线程1 | | 线程2 | | 线程3 |
+--------+ +--------+ +--------+ +--------+
| | | |
| await() | | |
| 等待... | | |
| | countDown() |
| | count=2 | |
| | | countDown()
| | | count=1 |
| | | | countDown()
| | | | count=0
| 唤醒! <---------------------------+
| 继续执行
v
1.2 基础使用示例
java
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
int threadCount = 5;
CountDownLatch latch = new CountDownLatch(threadCount);
System.out.println("主线程开始等待所有工作线程完成...");
// 启动多个工作线程
for (int i = 0; i < threadCount; i++) {
final int taskId = i;
new Thread(() -> {
try {
System.out.println("任务" + taskId + "开始执行");
// 模拟耗时操作
Thread.sleep((long) (Math.random() * 3000));
System.out.println("任务" + taskId + "执行完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 计数器减1
latch.countDown();
}
}, "Worker-" + i).start();
}
// 等待所有线程完成
latch.await();
System.out.println("所有任务已完成,主线程继续执行");
}
}
1.3 生产实战 - 批量数据初始化
java
import java.util.concurrent.*;
import java.util.List;
import java.util.ArrayList;
/**
* 应用启动时并行加载多个数据源
* 参考Spring Boot应用启动场景
*/
public class ApplicationStartup {
private final ExecutorService executor =
Executors.newFixedThreadPool(10);
public void initialize() {
System.out.println("应用启动中,开始初始化资源...");
long startTime = System.currentTimeMillis();
List<String> dataSources = List.of(
"用户数据", "配置数据", "缓存预热",
"数据库连接池", "消息队列"
);
CountDownLatch latch = new CountDownLatch(dataSources.size());
// 并行初始化各个数据源
for (String dataSource : dataSources) {
executor.submit(() -> {
try {
loadDataSource(dataSource);
} finally {
latch.countDown();
System.out.println(dataSource + " 初始化完成,剩余: " +
latch.getCount());
}
});
}
try {
// 等待所有数据源初始化完成,最多等待30秒
boolean completed = latch.await(30, TimeUnit.SECONDS);
if (completed) {
long duration = System.currentTimeMillis() - startTime;
System.out.println("所有资源初始化完成,耗时: " + duration + "ms");
} else {
System.err.println("初始化超时,部分资源未完成");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("初始化被中断");
} finally {
executor.shutdown();
}
}
private void loadDataSource(String dataSource) {
try {
System.out.println("开始加载: " + dataSource);
// 模拟加载耗时
Thread.sleep((long) (Math.random() * 3000));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) {
new ApplicationStartup().initialize();
}
}
1.4 实战案例 - 压测工具
java
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* 并发压测工具
* 模拟JMeter、Gatling等压测工具的实现
*/
public class ConcurrentStressTester {
private final AtomicInteger successCount = new AtomicInteger(0);
private final AtomicInteger failCount = new AtomicInteger(0);
private final AtomicLong totalResponseTime = new AtomicLong(0);
/**
* 执行压测
* @param concurrentUsers 并发用户数
* @param requestsPerUser 每个用户的请求数
*/
public void runStressTest(int concurrentUsers, int requestsPerUser) {
System.out.println("=== 压测开始 ===");
System.out.println("并发用户数: " + concurrentUsers);
System.out.println("每用户请求数: " + requestsPerUser);
CountDownLatch startLatch = new CountDownLatch(1);
CountDownLatch endLatch = new CountDownLatch(concurrentUsers);
long testStartTime = System.currentTimeMillis();
// 创建并发用户
for (int i = 0; i < concurrentUsers; i++) {
final int userId = i;
new Thread(() -> {
try {
// 等待统一开始信号
startLatch.await();
// 执行请求
for (int j = 0; j < requestsPerUser; j++) {
executeRequest(userId, j);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
endLatch.countDown();
}
}, "User-" + i).start();
}
// 所有线程准备就绪,统一开始
startLatch.countDown();
try {
// 等待所有请求完成
endLatch.await();
long testDuration = System.currentTimeMillis() - testStartTime;
// 输出统计结果
printStatistics(testDuration, concurrentUsers * requestsPerUser);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void executeRequest(int userId, int requestId) {
long startTime = System.currentTimeMillis();
try {
// 模拟HTTP请求
boolean success = mockHttpRequest();
long responseTime = System.currentTimeMillis() - startTime;
totalResponseTime.addAndGet(responseTime);
if (success) {
successCount.incrementAndGet();
} else {
failCount.incrementAndGet();
}
} catch (Exception e) {
failCount.incrementAndGet();
}
}
private boolean mockHttpRequest() {
try {
// 模拟网络延迟 50-200ms
Thread.sleep(50 + (long) (Math.random() * 150));
// 90%成功率
return Math.random() < 0.9;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
private void printStatistics(long duration, int totalRequests) {
int success = successCount.get();
int fail = failCount.get();
double successRate = (double) success / totalRequests * 100;
double avgResponseTime = (double) totalResponseTime.get() / totalRequests;
double tps = (double) totalRequests / duration * 1000;
System.out.println("\n=== 压测结果 ===");
System.out.println("总耗时: " + duration + "ms");
System.out.println("总请求数: " + totalRequests);
System.out.println("成功数: " + success);
System.out.println("失败数: " + fail);
System.out.println("成功率: " + String.format("%.2f%%", successRate));
System.out.println("平均响应时间: " + String.format("%.2fms", avgResponseTime));
System.out.println("TPS: " + String.format("%.2f", tps));
}
public static void main(String[] args) {
ConcurrentStressTester tester = new ConcurrentStressTester();
tester.runStressTest(100, 10);
}
}
二、CyclicBarrier - 循环栅栏
2.1 基本概念
CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点。与CountDownLatch不同,CyclicBarrier可以重复使用(循环)。
csharp
线程协作流程:
+--------+ +--------+ +--------+
| 线程1 | | 线程2 | | 线程3 |
+--------+ +--------+ +--------+
| | |
| await() | |
| 等待... | |
| | await() |
| | 等待... |
| | | await()
| <--------屏障点到达-----+
| 全部唤醒,继续执行
| | |
| 第二轮 | |
| await() | |
| ... | await() |
| | ... | await()
| <--------再次到达屏障---+
v v v
2.2 基础使用示例
java
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) {
int parties = 3;
// 创建栅栏,当3个线程都到达时执行屏障动作
CyclicBarrier barrier = new CyclicBarrier(parties, () -> {
System.out.println("所有线程已到达屏障,执行屏障动作");
System.out.println("========================\n");
});
// 启动3个线程
for (int i = 0; i < parties; i++) {
final int threadId = i;
new Thread(() -> {
try {
for (int round = 0; round < 2; round++) {
System.out.println("线程" + threadId + " 第" +
(round + 1) + "轮开始工作");
// 模拟工作
Thread.sleep((long) (Math.random() * 2000));
System.out.println("线程" + threadId + " 到达屏障,等待其他线程");
// 等待其他线程
barrier.await();
System.out.println("线程" + threadId + " 继续执行");
}
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}, "Thread-" + i).start();
}
}
}
2.3 CyclicBarrier vs CountDownLatch
scss
+-------------------+-------------------------+-------------------------+
| 特性 | CountDownLatch | CyclicBarrier |
+-------------------+-------------------------+-------------------------+
| 计数方式 | 递减到0 | 递增到parties |
| 是否可重用 | 不可重用 | 可重用 |
| 典型场景 | 等待其他线程完成 | 多线程协作分阶段执行 |
| 调用方式 | countDown() / await() | await() |
| 屏障动作 | 无 | 支持屏障到达时执行动作 |
| 异常处理 | 简单 | 需处理BrokenBarrierException |
+-------------------+-------------------------+-------------------------+
2.4 生产实战 - 并行计算框架
java
import java.util.concurrent.*;
import java.util.concurrent.atomic.DoubleAdder;
/**
* 并行计算框架 - 模拟MapReduce
* 参考Hadoop MapReduce的分治思想
*/
public class ParallelComputeFramework {
/**
* 并行计算大数组的总和
*/
public static double parallelSum(double[] array, int workerCount) {
int length = array.length;
int chunkSize = length / workerCount;
DoubleAdder totalSum = new DoubleAdder();
CyclicBarrier barrier = new CyclicBarrier(workerCount, () -> {
System.out.println("所有分片计算完成,开始汇总结果");
});
ExecutorService executor = Executors.newFixedThreadPool(workerCount);
CountDownLatch latch = new CountDownLatch(workerCount);
// Map阶段:分片计算
for (int i = 0; i < workerCount; i++) {
final int workerId = i;
final int start = i * chunkSize;
final int end = (i == workerCount - 1) ? length : (i + 1) * chunkSize;
executor.submit(() -> {
try {
// 计算当前分片
double partialSum = 0;
for (int j = start; j < end; j++) {
partialSum += array[j];
}
System.out.println("Worker-" + workerId + " 计算范围[" +
start + ", " + end + "), 部分和: " + partialSum);
// 等待其他Worker完成
barrier.await();
// Reduce阶段:汇总结果
totalSum.add(partialSum);
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
});
}
try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
executor.shutdown();
}
return totalSum.sum();
}
public static void main(String[] args) {
// 创建测试数据
int size = 10_000_000;
double[] array = new double[size];
for (int i = 0; i < size; i++) {
array[i] = i + 1;
}
// 串行计算
long start = System.currentTimeMillis();
double sum1 = 0;
for (double v : array) {
sum1 += v;
}
long serialTime = System.currentTimeMillis() - start;
System.out.println("串行计算结果: " + sum1 + ", 耗时: " + serialTime + "ms\n");
// 并行计算
start = System.currentTimeMillis();
double sum2 = parallelSum(array, 8);
long parallelTime = System.currentTimeMillis() - start;
System.out.println("\n并行计算结果: " + sum2 + ", 耗时: " + parallelTime + "ms");
System.out.println("加速比: " + String.format("%.2f",
(double) serialTime / parallelTime));
}
}
2.5 实战案例 - 多阶段任务协调
java
import java.util.concurrent.*;
import java.util.ArrayList;
import java.util.List;
/**
* 多阶段任务协调器
* 场景:数据ETL流程(Extract-Transform-Load)
*/
public class ETLPipeline {
private final int workerCount = 4;
private final List<String> extractedData = new CopyOnWriteArrayList<>();
private final List<String> transformedData = new CopyOnWriteArrayList<>();
public void runETL(List<String> sourceData) {
System.out.println("开始ETL流程,数据量: " + sourceData.size());
// 创建三阶段屏障
CyclicBarrier extractBarrier = new CyclicBarrier(workerCount, () -> {
System.out.println("\n=== Extract阶段完成 ===");
System.out.println("已提取数据: " + extractedData.size() + "条\n");
});
CyclicBarrier transformBarrier = new CyclicBarrier(workerCount, () -> {
System.out.println("\n=== Transform阶段完成 ===");
System.out.println("已转换数据: " + transformedData.size() + "条\n");
});
CyclicBarrier loadBarrier = new CyclicBarrier(workerCount, () -> {
System.out.println("\n=== Load阶段完成 ===");
System.out.println("ETL流程全部完成");
});
ExecutorService executor = Executors.newFixedThreadPool(workerCount);
int chunkSize = sourceData.size() / workerCount;
for (int i = 0; i < workerCount; i++) {
final int workerId = i;
final int start = i * chunkSize;
final int end = (i == workerCount - 1) ?
sourceData.size() : (i + 1) * chunkSize;
executor.submit(() -> {
try {
// 阶段1: Extract(提取)
System.out.println("Worker-" + workerId + " 开始Extract");
List<String> chunk = extractData(sourceData, start, end);
extractedData.addAll(chunk);
extractBarrier.await();
// 阶段2: Transform(转换)
System.out.println("Worker-" + workerId + " 开始Transform");
List<String> transformed = transformData(chunk);
transformedData.addAll(transformed);
transformBarrier.await();
// 阶段3: Load(加载)
System.out.println("Worker-" + workerId + " 开始Load");
loadData(transformed);
loadBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private List<String> extractData(List<String> source, int start, int end) {
List<String> result = new ArrayList<>();
for (int i = start; i < end; i++) {
// 模拟数据提取
sleepRandom(10);
result.add(source.get(i));
}
return result;
}
private List<String> transformData(List<String> data) {
List<String> result = new ArrayList<>();
for (String item : data) {
// 模拟数据转换
sleepRandom(15);
result.add(item.toUpperCase() + "_TRANSFORMED");
}
return result;
}
private void loadData(List<String> data) {
for (String item : data) {
// 模拟数据加载到数据库
sleepRandom(5);
}
}
private void sleepRandom(int maxMillis) {
try {
Thread.sleep((long) (Math.random() * maxMillis));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) {
List<String> sourceData = new ArrayList<>();
for (int i = 0; i < 100; i++) {
sourceData.add("data_" + i);
}
new ETLPipeline().runETL(sourceData);
}
}
三、Semaphore - 信号量
3.1 基本概念
Semaphore(信号量)用于控制同时访问特定资源的线程数量,它维护了一组许可证(permits),线程在访问资源前必须先获取许可证,使用完后释放许可证。
ini
资源访问控制:
Semaphore(3) <- 3个许可证
|
+----+----+----+
| | | |
许可1 许可2 许可3
| | |
v v v
+------+------+------+ 等待队列: [线程4, 线程5, ...]
|线程1 |线程2 |线程3 |
+------+------+------+
| | |
v v v
[资源] [资源] [资源]
3.2 基础使用示例
java
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
// 创建3个许可的信号量
Semaphore semaphore = new Semaphore(3);
// 启动10个线程竞争资源
for (int i = 0; i < 10; i++) {
final int threadId = i;
new Thread(() -> {
try {
System.out.println("线程" + threadId + " 等待获取许可...");
// 获取许可
semaphore.acquire();
System.out.println("线程" + threadId + " 获得许可,开始执行");
System.out.println("当前可用许可数: " +
semaphore.availablePermits());
// 模拟业务处理
Thread.sleep(2000);
System.out.println("线程" + threadId + " 执行完成,释放许可");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 释放许可
semaphore.release();
}
}, "Thread-" + i).start();
}
}
}
3.3 生产实战 - 数据库连接池
java
import java.util.concurrent.*;
import java.util.LinkedList;
/**
* 基于Semaphore实现的数据库连接池
* 参考HikariCP、Druid等连接池的实现思想
*/
public class DatabaseConnectionPool {
private final LinkedList<Connection> pool = new LinkedList<>();
private final Semaphore semaphore;
private final int poolSize;
public DatabaseConnectionPool(int poolSize) {
this.poolSize = poolSize;
this.semaphore = new Semaphore(poolSize);
// 初始化连接池
for (int i = 0; i < poolSize; i++) {
pool.add(new Connection(i));
}
}
/**
* 获取连接
*/
public Connection getConnection(long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
// 尝试获取许可
boolean acquired = semaphore.tryAcquire(timeout, unit);
if (!acquired) {
throw new TimeoutException("获取连接超时");
}
synchronized (pool) {
if (pool.isEmpty()) {
// 理论上不应该发生
semaphore.release();
throw new IllegalStateException("连接池为空");
}
return pool.removeFirst();
}
}
/**
* 释放连接
*/
public void releaseConnection(Connection connection) {
if (connection == null) {
return;
}
synchronized (pool) {
pool.addLast(connection);
}
// 释放许可
semaphore.release();
}
/**
* 获取连接池统计信息
*/
public String getStats() {
return String.format(
"连接池大小: %d, 可用连接: %d, 已用连接: %d",
poolSize,
semaphore.availablePermits(),
poolSize - semaphore.availablePermits()
);
}
/**
* 模拟数据库连接
*/
static class Connection {
private final int id;
public Connection(int id) {
this.id = id;
}
public void executeQuery(String sql) {
System.out.println("Connection-" + id + " 执行SQL: " + sql);
// 模拟查询耗时
try {
Thread.sleep((long) (Math.random() * 1000));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
@Override
public String toString() {
return "Connection-" + id;
}
}
/**
* 测试连接池
*/
public static void main(String[] args) {
DatabaseConnectionPool pool = new DatabaseConnectionPool(5);
ExecutorService executor = Executors.newFixedThreadPool(20);
// 模拟20个并发查询
for (int i = 0; i < 20; i++) {
final int queryId = i;
executor.submit(() -> {
Connection conn = null;
try {
System.out.println("查询" + queryId + " 请求连接...");
System.out.println(pool.getStats());
// 获取连接,最多等待3秒
conn = pool.getConnection(3, TimeUnit.SECONDS);
System.out.println("查询" + queryId + " 获得连接: " + conn);
// 执行查询
conn.executeQuery("SELECT * FROM users WHERE id = " + queryId);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("查询" + queryId + " 被中断");
} catch (TimeoutException e) {
System.err.println("查询" + queryId + " 获取连接超时");
} finally {
// 释放连接
if (conn != null) {
pool.releaseConnection(conn);
System.out.println("查询" + queryId + " 释放连接: " + conn);
}
}
});
}
executor.shutdown();
}
}
3.4 实战案例 - 限流器
java
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 基于Semaphore的限流器
* 参考Guava RateLimiter和Sentinel的实现
*/
public class RateLimiter {
private final Semaphore semaphore;
private final int maxPermits;
private final long timeWindowMillis;
private final ScheduledExecutorService scheduler;
private final AtomicInteger rejectedCount = new AtomicInteger(0);
/**
* @param maxPermits 时间窗口内最大请求数
* @param timeWindowMillis 时间窗口大小(毫秒)
*/
public RateLimiter(int maxPermits, long timeWindowMillis) {
this.maxPermits = maxPermits;
this.timeWindowMillis = timeWindowMillis;
this.semaphore = new Semaphore(maxPermits);
this.scheduler = Executors.newScheduledThreadPool(1);
// 定期补充许可
startRefillTask();
}
/**
* 尝试获取许可
*/
public boolean tryAcquire() {
boolean acquired = semaphore.tryAcquire();
if (!acquired) {
rejectedCount.incrementAndGet();
}
return acquired;
}
/**
* 尝试获取许可,带超时
*/
public boolean tryAcquire(long timeout, TimeUnit unit)
throws InterruptedException {
boolean acquired = semaphore.tryAcquire(timeout, unit);
if (!acquired) {
rejectedCount.incrementAndGet();
}
return acquired;
}
/**
* 启动许可补充任务
*/
private void startRefillTask() {
scheduler.scheduleAtFixedRate(() -> {
// 计算需要补充的许可数
int used = maxPermits - semaphore.availablePermits();
if (used > 0) {
semaphore.release(used);
System.out.println("补充许可: " + used +
", 当前可用: " + semaphore.availablePermits());
}
}, timeWindowMillis, timeWindowMillis, TimeUnit.MILLISECONDS);
}
/**
* 获取统计信息
*/
public String getStats() {
return String.format(
"最大许可: %d, 可用许可: %d, 已拒绝: %d",
maxPermits,
semaphore.availablePermits(),
rejectedCount.get()
);
}
public void shutdown() {
scheduler.shutdown();
}
/**
* 测试限流器
*/
public static void main(String[] args) throws InterruptedException {
// 创建限流器:每秒最多10个请求
RateLimiter limiter = new RateLimiter(10, 1000);
ExecutorService executor = Executors.newFixedThreadPool(20);
System.out.println("开始压测,尝试每秒发送50个请求...\n");
// 模拟高并发请求
for (int i = 0; i < 100; i++) {
final int requestId = i;
executor.submit(() -> {
if (limiter.tryAcquire()) {
System.out.println("请求" + requestId + " 通过限流");
// 模拟业务处理
try {
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
} else {
System.err.println("请求" + requestId + " 被限流");
}
});
// 控制发送速率
if (i % 10 == 9) {
Thread.sleep(200);
}
}
Thread.sleep(3000);
System.out.println("\n" + limiter.getStats());
executor.shutdown();
limiter.shutdown();
}
}
四、Exchanger - 数据交换器
4.1 基本概念
Exchanger用于两个线程之间交换数据,它提供了一个同步点,两个线程可以在这个点上交换彼此的数据。
diff
数据交换流程:
+----------+ +----------+
| 线程A | | 线程B |
+----------+ +----------+
| |
| 持有数据: DataA |
| | 持有数据: DataB
v v
+------------------------------------------+
| exchange() 同步点 |
+------------------------------------------+
| |
| 获得: DataB | 获得: DataA
v v
4.2 基础使用示例
java
import java.util.concurrent.Exchanger;
public class ExchangerDemo {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
// 线程A
new Thread(() -> {
try {
String data = "来自线程A的数据";
System.out.println("线程A准备交换数据: " + data);
// 交换数据
String received = exchanger.exchange(data);
System.out.println("线程A收到数据: " + received);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Thread-A").start();
// 线程B
new Thread(() -> {
try {
Thread.sleep(1000); // 延迟1秒
String data = "来自线程B的数据";
System.out.println("线程B准备交换数据: " + data);
// 交换数据
String received = exchanger.exchange(data);
System.out.println("线程B收到数据: " + received);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Thread-B").start();
}
}
4.3 生产实战 - 双缓冲技术
java
import java.util.concurrent.*;
import java.util.List;
import java.util.ArrayList;
/**
* 双缓冲技术实现
* 一个线程负责生成数据,另一个线程负责处理数据
* 使用Exchanger实现零拷贝的缓冲区交换
*/
public class DoubleBuffering {
private final Exchanger<List<String>> exchanger = new Exchanger<>();
private volatile boolean running = true;
/**
* 生产者线程:生成数据
*/
class Producer implements Runnable {
@Override
public void run() {
List<String> currentBuffer = new ArrayList<>();
int batchSize = 10;
int count = 0;
try {
while (running) {
// 生成数据
for (int i = 0; i < batchSize; i++) {
String data = "Data-" + (count++);
currentBuffer.add(data);
Thread.sleep(50); // 模拟数据生成耗时
}
System.out.println("生产者: 生成了" + currentBuffer.size() +
"条数据,准备交换缓冲区");
// 交换缓冲区
currentBuffer = exchanger.exchange(currentBuffer);
System.out.println("生产者: 获得空缓冲区,继续生产");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
/**
* 消费者线程:处理数据
*/
class Consumer implements Runnable {
@Override
public void run() {
List<String> currentBuffer = new ArrayList<>();
try {
while (running) {
// 交换缓冲区
currentBuffer = exchanger.exchange(currentBuffer);
System.out.println("消费者: 获得" + currentBuffer.size() +
"条数据,开始处理");
// 处理数据
for (String data : currentBuffer) {
processData(data);
}
System.out.println("消费者: 处理完成,清空缓冲区");
currentBuffer.clear();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void processData(String data) {
// 模拟数据处理
try {
Thread.sleep(30);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public void start() throws InterruptedException {
Thread producer = new Thread(new Producer(), "Producer");
Thread consumer = new Thread(new Consumer(), "Consumer");
producer.start();
consumer.start();
// 运行10秒后停止
Thread.sleep(10000);
running = false;
producer.join();
consumer.join();
}
public static void main(String[] args) throws InterruptedException {
new DoubleBuffering().start();
}
}
4.4 实战案例 - 遗传算法
java
import java.util.concurrent.*;
import java.util.*;
/**
* 遗传算法实现 - 使用Exchanger进行种群交换
* 模拟并行遗传算法求解优化问题
*/
public class GeneticAlgorithm {
private final Exchanger<Population> exchanger = new Exchanger<>();
private static final int POPULATION_SIZE = 100;
private static final int GENERATIONS = 50;
/**
* 种群
*/
static class Population {
List<Individual> individuals = new ArrayList<>();
public Individual getBest() {
return individuals.stream()
.max(Comparator.comparingDouble(Individual::getFitness))
.orElse(null);
}
public void addIndividual(Individual individual) {
individuals.add(individual);
}
public int size() {
return individuals.size();
}
}
/**
* 个体
*/
static class Individual {
double[] genes;
double fitness;
public Individual(int geneSize) {
genes = new double[geneSize];
Random random = new Random();
for (int i = 0; i < geneSize; i++) {
genes[i] = random.nextDouble() * 100;
}
calculateFitness();
}
private void calculateFitness() {
// 适应度函数:求基因值之和的平方根
double sum = 0;
for (double gene : genes) {
sum += gene;
}
fitness = Math.sqrt(sum);
}
public double getFitness() {
return fitness;
}
}
/**
* 进化岛(子种群)
*/
class EvolutionIsland implements Callable<Individual> {
private final int islandId;
private Population population;
public EvolutionIsland(int islandId) {
this.islandId = islandId;
this.population = initializePopulation();
}
private Population initializePopulation() {
Population pop = new Population();
for (int i = 0; i < POPULATION_SIZE / 2; i++) {
pop.addIndividual(new Individual(10));
}
return pop;
}
@Override
public Individual call() throws Exception {
for (int gen = 0; gen < GENERATIONS; gen++) {
// 进化
evolve();
// 每隔5代交换一次种群
if (gen % 5 == 0 && gen > 0) {
System.out.println("岛屿" + islandId + " 第" + gen +
"代,准备交换种群");
// 交换部分个体
population = exchanger.exchange(population,
1, TimeUnit.SECONDS);
System.out.println("岛屿" + islandId + " 完成种群交换");
}
// 输出当前最优解
if (gen % 10 == 0) {
Individual best = population.getBest();
System.out.println("岛屿" + islandId + " 第" + gen +
"代最优适应度: " +
String.format("%.2f", best.getFitness()));
}
}
return population.getBest();
}
private void evolve() {
// 选择、交叉、变异(简化实现)
Random random = new Random();
Population newPop = new Population();
while (newPop.size() < POPULATION_SIZE / 2) {
// 选择两个父代
Individual parent1 = population.individuals
.get(random.nextInt(population.size()));
Individual parent2 = population.individuals
.get(random.nextInt(population.size()));
// 生成子代(简化:直接选择适应度较高的)
Individual child = parent1.getFitness() > parent2.getFitness()
? parent1 : parent2;
newPop.addIndividual(child);
}
population = newPop;
}
}
/**
* 运行并行遗传算法
*/
public void run() throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(2);
// 创建两个进化岛
EvolutionIsland island1 = new EvolutionIsland(1);
EvolutionIsland island2 = new EvolutionIsland(2);
// 并行进化
Future<Individual> future1 = executor.submit(island1);
Future<Individual> future2 = executor.submit(island2);
// 获取结果
Individual best1 = future1.get();
Individual best2 = future2.get();
System.out.println("\n=== 最终结果 ===");
System.out.println("岛屿1最优适应度: " +
String.format("%.2f", best1.getFitness()));
System.out.println("岛屿2最优适应度: " +
String.format("%.2f", best2.getFitness()));
Individual globalBest = best1.getFitness() > best2.getFitness()
? best1 : best2;
System.out.println("全局最优适应度: " +
String.format("%.2f", globalBest.getFitness()));
executor.shutdown();
}
public static void main(String[] args) throws Exception {
new GeneticAlgorithm().run();
}
}
五、Phaser - 阶段器
5.1 基本概念
Phaser是JDK 7引入的同步工具,它比CyclicBarrier更加灵活,支持动态调整参与者数量,并且可以控制多个阶段的执行。
lua
Phaser工作流程:
Phase 0 Phase 1 Phase 2
| | |
+-----+-----+ +-----+-----+ +-----+-----+
| | | | | | | | |
线程1 线程2 线程3 线程1 线程2 线程3 线程1 线程2 线程3
| | | | | | | | |
+-----+-----+ +-----+-----+ +-----+-----+
| | |
arriveAndAwaitAdvance()
5.2 基础使用示例
java
import java.util.concurrent.Phaser;
public class PhaserDemo {
public static void main(String[] args) {
// 创建Phaser,3个参与者
Phaser phaser = new Phaser(3) {
@Override
protected boolean onAdvance(int phase, int registeredParties) {
System.out.println("==== 阶段" + phase + "完成 ====\n");
// 返回true表示终止,false表示继续
return phase >= 2; // 执行3个阶段后终止
}
};
// 启动3个线程
for (int i = 0; i < 3; i++) {
final int threadId = i;
new Thread(() -> {
for (int phase = 0; phase < 3; phase++) {
System.out.println("线程" + threadId + " 阶段" + phase + "开始");
// 模拟工作
try {
Thread.sleep((long) (Math.random() * 2000));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("线程" + threadId + " 阶段" + phase + "完成");
// 等待其他线程完成当前阶段
phaser.arriveAndAwaitAdvance();
}
}, "Thread-" + i).start();
}
}
}
5.3 生产实战 - 批处理任务
java
import java.util.concurrent.*;
import java.util.*;
/**
* 分阶段批处理任务
* 场景:大数据处理流水线
*/
public class BatchProcessingPipeline {
/**
* 数据处理任务
*/
static class DataProcessor implements Runnable {
private final Phaser phaser;
private final int processorId;
private final List<String> data;
public DataProcessor(Phaser phaser, int processorId, List<String> data) {
this.phaser = phaser;
this.processorId = processorId;
this.data = data;
phaser.register(); // 注册到phaser
}
@Override
public void run() {
try {
// 阶段1:数据验证
validateData();
phaser.arriveAndAwaitAdvance();
// 阶段2:数据清洗
cleanData();
phaser.arriveAndAwaitAdvance();
// 阶段3:数据转换
transformData();
phaser.arriveAndAwaitAdvance();
// 阶段4:数据保存
saveData();
phaser.arriveAndAwaitAdvance();
} finally {
phaser.arriveAndDeregister(); // 注销
}
}
private void validateData() {
System.out.println("处理器" + processorId + " 验证数据: " + data.size() + "条");
sleep(500);
}
private void cleanData() {
System.out.println("处理器" + processorId + " 清洗数据");
sleep(800);
}
private void transformData() {
System.out.println("处理器" + processorId + " 转换数据");
sleep(1000);
}
private void saveData() {
System.out.println("处理器" + processorId + " 保存数据");
sleep(600);
}
private void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public static void main(String[] args) {
// 创建Phaser
Phaser phaser = new Phaser(1) { // 主线程也参与
@Override
protected boolean onAdvance(int phase, int registeredParties) {
String[] phases = {"验证", "清洗", "转换", "保存"};
System.out.println("\n==== " + phases[phase] + "阶段完成 ====\n");
return phase >= 3; // 4个阶段后终止
}
};
// 准备测试数据
List<List<String>> batches = new ArrayList<>();
for (int i = 0; i < 5; i++) {
List<String> batch = new ArrayList<>();
for (int j = 0; j < 100; j++) {
batch.add("data_" + (i * 100 + j));
}
batches.add(batch);
}
// 创建处理器
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < batches.size(); i++) {
executor.submit(new DataProcessor(phaser, i, batches.get(i)));
}
// 主线程参与协调
for (int phase = 0; phase < 4; phase++) {
phaser.arriveAndAwaitAdvance();
}
phaser.arriveAndDeregister(); // 主线程注销
executor.shutdown();
System.out.println("\n所有批次处理完成");
}
}
六、并发工具综合对比
6.1 功能对比表
diff
+------------------+------------------+------------------+------------------+
| 工具 | 使用场景 | 特点 | 复杂度 |
+------------------+------------------+------------------+------------------+
| CountDownLatch | 等待多个线程完成 | 一次性,不可重用 | 简单 |
| | 主线程等待子线程 | 计数递减到0 | |
+------------------+------------------+------------------+------------------+
| CyclicBarrier | 多线程协作 | 可循环使用 | 中等 |
| | 分阶段执行 | 支持屏障动作 | |
+------------------+------------------+------------------+------------------+
| Semaphore | 资源访问控制 | 限制并发数 | 简单 |
| | 限流 | 可获取多个许可 | |
+------------------+------------------+------------------+------------------+
| Exchanger | 线程间数据交换 | 仅支持两个线程 | 简单 |
| | 双缓冲 | 同步交换数据 | |
+------------------+------------------+------------------+------------------+
| Phaser | 动态多阶段协作 | 最灵活 | 复杂 |
| | 可动态注册/注销 | 支持分层 | |
+------------------+------------------+------------------+------------------+
6.2 选型建议
java
/**
* 并发工具选型指南
*/
public class ConcurrencyToolSelection {
/**
* 场景1: 等待所有子任务完成 -> CountDownLatch
*/
public void scenario1() {
// 主线程启动多个子线程,等待所有子线程完成后继续
// 示例:应用启动时加载多个资源
}
/**
* 场景2: 多线程循环协作 -> CyclicBarrier
*/
public void scenario2() {
// 多个线程需要在多个阶段进行同步
// 示例:并行计算,每轮计算后同步结果
}
/**
* 场景3: 限制资源访问数量 -> Semaphore
*/
public void scenario3() {
// 限制同时访问某资源的线程数
// 示例:数据库连接池、限流器
}
/**
* 场景4: 两线程交换数据 -> Exchanger
*/
public void scenario4() {
// 两个线程需要在某个点交换数据
// 示例:生产者-消费者模式、双缓冲技术
}
/**
* 场景5: 复杂多阶段协调 -> Phaser
*/
public void scenario5() {
// 多个线程需要经过多个阶段,且参与者数量可能动态变化
// 示例:批处理流水线、并行迭代算法
}
}
七、在开源框架中的应用
7.1 Spring Cloud Gateway限流
java
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
import java.util.concurrent.Semaphore;
/**
* 模拟Spring Cloud Gateway的限流实现
*/
public class GatewayRateLimiter {
private final Semaphore semaphore;
public GatewayRateLimiter(int capacity) {
this.semaphore = new Semaphore(capacity);
}
public boolean allowRequest() {
return semaphore.tryAcquire();
}
public void releaseRequest() {
semaphore.release();
}
/**
* 处理请求
*/
public String handleRequest(String requestId) {
if (allowRequest()) {
try {
System.out.println("请求" + requestId + "通过网关");
// 处理业务逻辑
Thread.sleep(100);
return "Success";
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return "Interrupted";
} finally {
releaseRequest();
}
} else {
System.err.println("请求" + requestId + "被限流");
return "RateLimited";
}
}
}
7.2 Dubbo服务启动
java
/**
* 模拟Dubbo服务启动过程中使用CountDownLatch
*/
public class DubboServiceBootstrap {
public void start() {
System.out.println("Dubbo服务启动中...");
int serviceCount = 5;
CountDownLatch latch = new CountDownLatch(serviceCount);
// 并行导出多个服务
String[] services = {
"UserService", "OrderService", "ProductService",
"PaymentService", "NotificationService"
};
ExecutorService executor = Executors.newFixedThreadPool(serviceCount);
for (String service : services) {
executor.submit(() -> {
try {
exportService(service);
} finally {
latch.countDown();
}
});
}
try {
latch.await(30, TimeUnit.SECONDS);
System.out.println("所有服务导出完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("服务启动被中断");
} finally {
executor.shutdown();
}
}
private void exportService(String serviceName) {
System.out.println("导出服务: " + serviceName);
try {
Thread.sleep((long) (Math.random() * 2000));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(serviceName + " 导出完成");
}
}
八、最佳实践与注意事项
8.1 常见陷阱
java
public class CommonPitfalls {
/**
* 陷阱1: CountDownLatch计数错误导致永久等待
*/
public void pitfall1() {
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 2; i++) { // 只启动2个线程,但计数是3
new Thread(() -> {
latch.countDown();
}).start();
}
try {
latch.await(); // 永远等待!
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* 解决方案:使用超时版本的await
*/
public void pitfall1Solution() {
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 2; i++) {
new Thread(() -> {
latch.countDown();
}).start();
}
try {
boolean completed = latch.await(5, TimeUnit.SECONDS);
if (!completed) {
System.err.println("等待超时,某些任务未完成");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* 陷阱2: Semaphore未正确释放导致资源泄漏
*/
public void pitfall2() {
Semaphore semaphore = new Semaphore(5);
try {
semaphore.acquire();
// 业务代码
if (Math.random() > 0.5) {
throw new RuntimeException("业务异常");
}
semaphore.release(); // 异常时无法执行!
} catch (Exception e) {
// 资源泄漏
}
}
/**
* 解决方案:在finally中释放
*/
public void pitfall2Solution() {
Semaphore semaphore = new Semaphore(5);
try {
semaphore.acquire();
// 业务代码
if (Math.random() > 0.5) {
throw new RuntimeException("业务异常");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
semaphore.release(); // 确保释放
}
}
/**
* 陷阱3: CyclicBarrier屏障破损
*/
public void pitfall3() {
CyclicBarrier barrier = new CyclicBarrier(3);
for (int i = 0; i < 3; i++) {
final int id = i;
new Thread(() -> {
try {
if (id == 1) {
throw new RuntimeException("线程1异常");
}
barrier.await(); // 其他线程会抛出BrokenBarrierException
} catch (Exception e) {
System.err.println("线程" + id + "异常: " + e.getMessage());
}
}).start();
}
}
/**
* 解决方案:捕获BrokenBarrierException并重置
*/
public void pitfall3Solution() {
CyclicBarrier barrier = new CyclicBarrier(3);
for (int i = 0; i < 3; i++) {
final int id = i;
new Thread(() -> {
try {
barrier.await();
} catch (BrokenBarrierException e) {
System.err.println("屏障已破损,重置");
barrier.reset();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
}
8.2 性能优化建议
sql
+------------------------------------------------------------------+
| 并发工具使用建议 |
+------------------------------------------------------------------+
| 1. 合理选择工具 |
| - 根据实际场景选择最合适的工具 |
| - 避免过度设计,简单场景用简单工具 |
+------------------------------------------------------------------+
| 2. 设置超时时间 |
| - await/acquire操作使用带超时的版本 |
| - 避免无限等待导致系统hang住 |
+------------------------------------------------------------------+
| 3. 正确处理中断 |
| - 捕获InterruptedException后恢复中断状态 |
| - 不要吞掉中断异常 |
+------------------------------------------------------------------+
| 4. 资源释放 |
| - 在finally块中释放资源 |
| - 使用try-with-resources(如果支持) |
+------------------------------------------------------------------+
| 5. 监控与日志 |
| - 记录关键节点的日志 |
| - 监控等待时间、成功率等指标 |
+------------------------------------------------------------------+
总结
Java并发工具为我们提供了强大的多线程协作能力:
- CountDownLatch:适用于等待多个线程完成任务的场景,如应用启动、批量任务等
- CyclicBarrier:适用于多线程分阶段协作,可循环使用
- Semaphore:适用于控制资源访问数量,如连接池、限流器
- Exchanger:适用于两线程间数据交换,如双缓冲、遗传算法
- Phaser:最灵活的工具,支持动态参与者和多阶段协调
在实际应用中,需要根据具体场景选择合适的工具,并注意:
- 设置合理的超时时间
- 正确处理异常和中断
- 确保资源正确释放
- 添加监控和日志