应优先使用 java.util.concurrent 显式并发工具替代 synchronized:计数用 AtomicInteger/LongAdder,状态变更用 ReentrantLock,协作用 CountDownLatch/CyclicBarrier/Phaser,线程池需按任务类型配置参数,volatile 仅适用于单次读写且不依赖当前值的场景,异步需注意上下文传递与资源泄漏。

用 java.util.concurrent 替代 synchronized 手动加锁
直接在方法或代码块上套 synchronized 是最易写错的并发起点——它隐式使用对象监视器,容易引发锁竞争、死锁,且无法响应中断、无法超时。真正高效的并发逻辑应优先选用 java.util.concurrent 包下的显式工具。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 计数场景用
AtomicInteger或LongAdder(高并发下后者性能更好) - 共享状态变更用
ReentrantLock,配合tryLock(long, TimeUnit)避免无限等待 - 线程协作优先选
CountDownLatch(一次性)、CyclicBarrier(可重用)、Phaser(动态注册)而非wait/notify - 所有锁操作必须配对:
lock()后紧跟try/finally中的unlock(),否则极易泄漏锁
线程池不是越大越好:合理配置 ThreadPoolExecutor
常见错误是把 Executors.newFixedThreadPool(100) 当万能解,结果 CPU 被上下文切换拖垮,或堆内存因堆积大量待执行任务而 OOM。线程池参数必须贴合任务类型。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- CPU 密集型任务:核心线程数 ≈
Runtime.getRuntime().availableProcessors(),最大线程数不宜超过该值 + 1 - IO 密集型任务:核心线程数可设为
2 * availableProcessors(),但必须设置合理的keepAliveTime(如 60L,TimeUnit.SECONDS)及时回收空闲线程 - 拒绝策略别用默认的
AbortPolicy(抛RejectedExecutionException),生产环境推荐CallerRunsPolicy或自定义日志+降级逻辑 - 务必传入有意义的
ThreadFactory,避免线程名全是pool-1-thread-1,排查问题时完全无法定位来源
慎用 volatile,它不保证复合操作的原子性
volatile 常被误认为“轻量级同步”,但它只提供可见性和禁止指令重排序,对 i++、list.add() 这类读-改-写操作毫无保护作用。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 仅当变量满足“单次读或单次写,且不依赖当前值”时才用
volatile,例如开关标志volatile boolean running = true -
i++必须换成AtomicInteger.incrementAndGet() - 集合操作不要用
volatile List,改用CopyOnWriteArrayList(读多写少)或ConcurrentHashMap(通用高并发) - 对象引用声明为
volatile只能保证引用本身可见,不保证其内部字段的可见性
异步非阻塞 ≠ 丢给线程池就完事:注意上下文与资源泄漏
Spring 的 @Async、CompletableFuture 链式调用看似简单,但常忽略事务传播、MDC 日志上下文丢失、数据库连接未归还等问题。
实操建议:
立即学习“Java免费学习笔记(深入)”;
-
@Async方法不能是 private/protected,也不能在同一个类内自调用(代理失效) - 异步任务中需手动传递
MDC.getCopyOfContextMap(),并在子线程开头用MDC.setContextMap()恢复,否则日志链路断裂 - 涉及数据库操作时,避免在异步线程中复用主线程的
Connection或EntityManager;JPA 场景下应启用spring.jpa.open-in-view=false并明确管理事务边界 - CompletableFuture 异常必须显式处理:
whenComplete或exceptionally,否则异常会静默吞掉,连Future.get()都捕获不到
真正的难点不在 API 调用,而在判断“这个状态是否需要同步”“这条路径会不会跨线程逃逸”“这个对象生命周期谁负责清理”。这些地方没有银弹,只有反复推演和压测验证。











