导语
多线程编程是Java开发的关键技能,合理运用能显著提升系统性能。本文分享5个经过验证的并发编程技巧,涵盖线程管理、锁优化和并发集合等核心主题,附可直接复用的代码模板,帮助您编写更稳健高效的多线程应用。
一、线程池最佳配置策略
场景:避免资源耗尽和任务堆积
优化方案:
// 根据任务类型配置线程池
int corePoolSize = Runtime.getRuntime().availableProcessors();
int maxPoolSize = corePoolSize * 4;
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000), // 有界队列
new ThreadFactoryBuilder().setNameFormat("app-thread-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 监控关键指标
executor.setRejectedExecutionHandler((r, pool) ->
metrics.rejectedTasks.increment()
);
配置原则:
- CPU密集型:线程数 = CPU核心数 + 1
- IO密集型:线程数 = CPU核心数 × 2
- 使用有界队列防止OOM
- 设置合理的线程名称便于监控
二、锁粒度优化实战
场景:高并发场景减少锁竞争
错误案例:
public synchronized void updateUser(User user) { // 全局锁
// 业务逻辑
}
分段锁优化:
private final Striped<Lock> locks = Striped.lock(32); // Guava分段锁
public void updateUser(Long userId, User user) {
Lock lock = locks.get(userId % 32);
lock.lock();
try {
// 只锁定单个用户操作
internalUpdate(user);
} finally {
lock.unlock();
}
}
// 无锁方案
private final AtomicReference<User> currentUser = new AtomicReference<>();
public void updateUser(User newUser) {
User prev;
do {
prev = currentUser.get();
} while (!currentUser.compareAndSet(prev, newUser));
}
性能对比:
| 方案 | 100并发QPS | 延迟(ms) |
|-------------|------------|----------|
| 全局锁 | 1200 | 83 |
| 分段锁 | 9800 | 12 |
| 无锁方案 | 14200 | 8 |
三、并发集合高效使用
场景:多线程共享数据访问
安全方案:
// 1. 高效计数
LongAdder counter = new LongAdder();
counter.increment(); // 优于AtomicLong
// 2. 并发映射
ConcurrentMap<String, User> userCache = new ConcurrentHashMap<>();
userCache.compute("user1", (k, v) ->
v == null ? new User() : v.updateLastAccess()
);
// 3. 写时复制列表
CopyOnWriteArrayList<LogEntry> logEntries = new CopyOnWriteArrayList<>();
logEntries.add(newEntry); // 读多写少场景
选型指南:
| 场景 | 推荐实现 | 特点 |
|---------------|-------------------------|-------------------|
| 高频计数器 | LongAdder | 分段计数 |
| 缓存映射 | ConcurrentHashMap | 高并发读写 |
| 事件监听器 | CopyOnWriteArrayList | 无锁遍历 |
四、线程间通信优化
场景:生产者-消费者模式高效实现
阻塞队列方案:
BlockingQueue<Task> queue = new LinkedBlockingDeque<>(1000);
// 生产者
public void produce(Task task) throws InterruptedException {
queue.put(task); // 阻塞添加
}
// 消费者
public void consume() {
while (true) {
Task task = queue.take(); // 阻塞获取
process(task);
}
}
// 多消费者优化
ExecutorService consumerPool = Executors.newFixedThreadPool(4);
for (int i = 0; i < 4; i++) {
consumerPool.submit(this::consume);
}
性能要点:
- 根据吞吐量调整队列容量
- 消费者线程数应与处理能力匹配
- 使用poll(timeout)避免永久阻塞
五、CompletableFuture异步编排
场景:复杂异步任务流程管理
链式调用示例:
CompletableFuture.supplyAsync(this::fetchUser, ioPool)
.thenApplyAsync(this::enrichUserData, cpuPool)
.thenAcceptAsync(this::sendNotification, ioPool)
.exceptionally(ex -> {
log.error("处理失败", ex);
return null;
});
// 组合多个异步任务
CompletableFuture<Order> orderFuture = fetchOrderAsync();
CompletableFuture<Inventory> inventoryFuture = fetchInventoryAsync();
orderFuture.thenCombine(inventoryFuture, (order, inventory) -> {
return checkStock(order, inventory);
}).thenAccept(this::processResult);
最佳实践:
- 指定不同线程池执行不同类型任务
- 使用handle统一处理结果和异常
- 避免嵌套过深影响可读性