VJTools项目中的并发处理最佳实践指南
前言
并发编程是Java开发中非常重要的一个领域,也是容易出现问题的重灾区。VJTools项目总结了一套关于并发处理的规范和实践经验,本文将对这些内容进行系统梳理和扩展讲解,帮助开发者更好地理解和应用这些并发编程的最佳实践。
一、线程创建与管理规范
1.1 线程命名的重要性
在创建线程或线程池时,必须为线程指定有意义的名称。这不仅仅是规范问题,更是线上问题排查的重要依据。
正确做法:
- 单线程直接设置名称
- 线程池使用ThreadFactory指定命名规则
// 单线程命名
Thread t = new Thread();
t.setName("order-process-thread");
// 线程池命名
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("payment-pool-%d").build();
1.2 优先使用线程池
直接创建线程会导致资源管理失控,应该始终优先使用线程池:
// 错误示范
new Thread(task).start();
// 正确做法
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.execute(task);
对于定时任务,也应该使用ScheduledExecutorService而非Timer类,因为Timer存在单线程执行和异常传播的问题。
二、线程池的正确使用
2.1 避免使用Executors创建线程池
Executors工具类虽然方便,但隐藏了资源耗尽的风险:
| 线程池类型 | 问题 | |------------|------| | FixedThreadPool/SingleThreadPool | 无界队列可能导致OOM | | CachedThreadPool/ScheduledThreadPool | 无界线程数可能导致OOM |
推荐做法:
new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(queueSize),
threadFactory,
new ThreadPoolExecutor.AbortPolicy()
);
2.2 优雅停止线程池
正确的线程池关闭流程:
- 先调用shutdown()拒绝新任务
- 再调用shutdownNow()中断正在执行的任务
- 可配合awaitTermination()等待任务结束
三、线程安全编程实践
3.1 正确处理线程中断
实现可中断的Runnable需要注意:
- 在可能阻塞的操作前检查中断状态
- 正确处理InterruptedException
- 不要吞掉中断异常
错误示例:
public void run() {
while(true) { // 未检查中断状态
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
logger.warn("Interrupted", e); // 吞掉异常
}
}
}
正确实现:
public void run() {
try {
while (!Thread.interrupted()) {
// 业务逻辑
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
}
}
3.2 捕获所有运行时异常
未捕获的异常会导致:
- 定时任务中断执行
- 线程池线程终止
- 日志丢失(仅打印到System.err)
解决方案:
executor.execute(() -> {
try {
// 业务代码
} catch (Exception e) {
logger.error("Task failed", e);
}
});
四、并发工具与模式
4.1 ThreadLocal的正确使用
对于非线程安全的类(如SimpleDateFormat),可以使用ThreadLocal:
private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
public String formatDate(Date date) {
return DATE_FORMAT.get().format(date);
}
注意每次使用前可能需要重置状态(如MessageDigest)。
4.2 锁优化技巧
- 锁粒度:尽量缩小锁范围
- 锁类型:优先使用对象锁而非类锁
- 锁分离:读写锁、分段锁等
- 无锁结构:Atomic类、ConcurrentHashMap等
4.3 volatile与原子类
- volatile解决可见性问题
- 原子类解决复合操作的原子性问题
// 可见性
private volatile boolean running = true;
// 原子操作
private AtomicInteger counter = new AtomicInteger();
counter.incrementAndGet();
五、高级并发模式
5.1 延迟初始化方案
不推荐使用双重检查锁,推荐静态内部类方式:
public class Singleton {
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
5.2 避免死锁
- 保持加锁顺序一致
- 使用tryLock带超时机制
- 避免嵌套锁
总结
VJTools项目中总结的这些并发处理规范,涵盖了从线程创建、线程池使用到高级并发模式等多个方面。遵循这些最佳实践可以帮助开发者:
- 编写更安全的并发代码
- 避免常见的并发陷阱
- 提高系统稳定性和性能
- 便于问题排查和诊断
在实际开发中,应当根据具体场景选择合适的并发策略,并在性能与安全性之间取得平衡。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考