线程安全的关键在于精准识别真正需保护的可变共享资源(如计数器、库存、会话数据),并按场景选用最轻量机制:读多写少用读写锁,纯状态用原子类,高并发用无锁或分段容器,同时严格缩小临界区、避免锁内阻塞,并通过线程池统一管控并发资源。

关键在于只在真正需要的地方同步,且用最轻量的方式实现。安全和性能不是非此即彼的选择,而是通过精准识别共享、合理控制粒度、分场景选用机制来达成动态平衡。
明确哪些变量或操作必须线程安全
不是所有变量都需要保护。只有被多个线程读写、且读写之间存在依赖关系的共享状态才构成风险。例如:
- 局部变量天然线程私有,无需加锁;
- 只读的全局配置(如初始化后不再修改的常量)也不需同步;
- 真正要盯住的是:计数器、缓存状态、订单库存、用户会话数据等可变共享资源。
判断标准很简单:如果去掉同步后,运行多次结果不一致,或出现 NullPointerException、Stale Data、Lost Update 等现象,就说明这里存在线程安全缺口。
按场景选对同步机制,避免“一把锁打天下”
不同访问模式对应不同工具,硬套 synchronized 往往是性能瓶颈的起点:
-
简单互斥 + 低竞争:用
synchronized方法或代码块,JVM 自动优化(偏向锁/轻量级锁),开发成本最低; -
读多写少(如商品库存查询 vs 扣减):用
ReentrantReadWriteLock,让并发读不阻塞; -
纯计数或状态标志(如请求总数、开关标记):优先用
AtomicInteger、AtomicBoolean,CAS 无锁更高效; -
需要超时、中断或灵活条件等待(如生产者-消费者):用
ReentrantLock配合Condition。
缩小锁范围,只包裹真正共享的部分
常见错误是把日志、参数校验、本地计算等非共享逻辑也包进同步块。这会人为拉长锁持有时间,增加线程排队概率。
正确做法是:
- 把共享变量的读-改-写逻辑单独抽出来,最小化临界区;
- 避免在同步块内调用外部方法(尤其可能阻塞或抛异常的);
- 像
ConcurrentHashMap这类容器,内部已做分段或 CAS 优化,直接使用比自己加锁包装HashMap更安全高效。
用线程池代替频繁创建线程,把并发控制收口到资源层
线程本身是昂贵资源。盲目开新线程不仅消耗内存(默认栈1MB),还引发大量上下文切换——当线程数远超 CPU 核心数时,系统反而变慢。
- CPU 密集型任务:线程数 ≈ CPU 核心数(如 8 核设 8~10 线程);
- I/O 密集型任务:可适当提高(如 8 核设 20~50 线程),利用等待时间重叠执行;
- 务必使用
ThreadPoolExecutor,而非Executors工厂方法,以便精细控制队列类型(如LinkedBlockingQueue或SynchronousQueue)、拒绝策略(如CallerRunsPolicy防雪崩)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











