java并发编程反模式本质是因缺乏线程/内存模型及工具语义理解导致的危险设计,须坚持“用对工具、锁对范围、管住状态”:禁用new thread而用有界队列线程池;共享资源须共用同一锁对象并缩小同步块;弃用thread.sleep()改用countdownlatch等juc工具;线程异常必须显式捕获或统一处理。

Java 并发编程常见反模式,本质是开发者在缺乏对线程模型、内存模型和工具类语义深入理解时,做出的“看似能跑、实则危险”的设计选择。避免它们不靠死记硬背,而在于抓住三个关键:用对工具、锁对范围、管住状态。
别手动 new Thread,统一走线程池
每次 new Thread().start() 都会创建新栈(默认约1MB),频繁调用极易触发 OutOfMemoryError: unable to create new native thread。更严重的是,线程生命周期失控,无法复用、无法监控、无法优雅关闭。
- 用
ThreadPoolExecutor显式构造,明确设置核心线程数、最大线程数、队列容量和拒绝策略 - CPU 密集型任务:核心线程数建议设为
Runtime.getRuntime().availableProcessors() + 1 - IO 密集型任务:可适当提高,但必须配**有界队列**(如
new ArrayBlockingQueue(200)),禁用无界LinkedBlockingQueue默认构造 - 避免
Executors.newCachedThreadPool()—— 它在突发流量下会无限创建线程,生产环境应视为禁用
别拿 this 或随机对象当锁,共享资源要共用同一把锁
同步失效往往不是没加 synchronized,而是锁对象错了。比如每个线程持有一个 TicketWindow 实例并锁 this,实际等于没锁——各锁各的,毫无互斥效果。
- 静态共享资源,用
private static final Object LOCK = new Object()作为唯一锁对象 - 避免锁
String、Integer等可能被常量池复用的对象,防止意外锁竞争 - 缩小同步范围:只把真正访问/修改共享变量的代码包进同步块,不要把
Thread.sleep()或日志等无关操作也锁进去 - 优先考虑无锁方案:计数类用
AtomicInteger,集合类用ConcurrentHashMap或CopyOnWriteArrayList,比手写锁更安全高效
别用 Thread.sleep() 做线程协调,用 JUC 同步工具代替
Thread.sleep(5000) 是“盲等”——不管任务是否完成,就卡死5秒。这既浪费资源,又无法响应变化,还容易掩盖真正的执行依赖关系。
- 等待多个线程完成:用
CountDownLatch(一次性)或CyclicBarrier(可重用) - 等待某个条件成立:用
Condition配合ReentrantLock,比wait()/notify()更可控 - 等待另一个线程结束:直接调用
thread.join(),语义清晰且可设超时 - 所有等待操作都应响应中断:在循环中定期检查
Thread.currentThread().isInterrupted(),及时退出
别让异常在线程里静默消失,必须显式处理或传播
线程中未捕获的异常会直接终止该线程,且不会向上传播到启动它的代码,导致问题“悄无声息”——任务失败了,日志没报,监控没响,系统却越来越慢。
- 为线程池配置
ThreadFactory,设置UncaughtExceptionHandler统一记录异常 - 在
Runnable或Callable内部用 try-catch 包裹全部逻辑,至少打一条 ERROR 日志 - 使用
Future.get()获取结果时,必须处理ExecutionException,它包装了原始异常 - 避免吞掉异常:不要写
catch (Exception e) { }或只写e.printStackTrace()
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











