Java线程同步的根本原因是防止多线程并发访问共享资源导致数据不一致、丢失更新等问题,需保障可见性、原子性和有序性;synchronized兼顾三者,volatile仅保障可见性与有序性但不保证原子性;锁对象和粒度影响安全性与性能;ConcurrentHashMap、AtomicXXX、ReentrantLock和ThreadLocal等提供了更高效灵活的同步方案。

Java中线程需要同步,根本原因是多个线程并发访问共享资源时,可能产生数据不一致、丢失更新、脏读等并发安全问题。同步不是为了“让程序变慢”,而是为了保证多线程环境下共享状态的可见性、原子性和有序性。
共享变量引发竞态条件(Race Condition)
当多个线程同时读写同一个变量(如静态字段、堆中对象的成员变量),且操作本身不是原子的(例如 i++ 包含读取、加1、写回三步),就可能出现竞态:两个线程几乎同时读到旧值,各自+1后再写回,结果只增加了一次,而非两次。
- 典型例子:int count = 0; 在两个线程中各执行1000次 count++,最终结果很可能小于2000
- 原因:JVM指令重排序 + CPU缓存不一致 + 操作非原子,导致线程间看不到彼此的修改
synchronized 和 volatile 的分工
synchronized 解决的是原子性 + 可见性 + 有序性;volatile 只保障可见性 + 有序性(禁止指令重排),但不保证原子性。
- 对简单赋值(如 flag = true)且无需复合操作,用 volatile 足够轻量
- 涉及读-改-写(如计数、状态切换、集合增删),必须用 synchronized、ReentrantLock 或原子类(AtomicInteger等)
- 错误用法:仅用 volatile int count 代替 count++ —— 仍会丢失更新
锁的对象和粒度影响安全性与性能
同步是否生效,取决于多个线程是否竞争同一把锁。锁对象错了,等于没锁;粒度太粗,又会严重串行化。
立即学习“Java免费学习笔记(深入)”;
- 实例方法加 synchronized → 锁的是当前实例(this),不同对象互不影响
- 静态方法加 synchronized → 锁的是该类的 Class 对象,所有实例共用一把锁
- 手动指定锁对象时,务必确保所有相关代码使用同一个引用(不能每次 new 一个新对象)
除了 synchronized,还有更灵活的同步方式
Java 提供了 java.util.concurrent 包下的多种线程安全工具,它们在特定场景下比传统锁更高效、更易用。
- ConcurrentHashMap:分段锁或CAS实现,支持高并发读写,无需外部同步
- AtomicXXX 类:基于CPU底层指令(如CAS),无锁实现原子操作,适合简单状态更新
- ReentrantLock:支持可中断、超时、公平锁、多条件队列,比 synchronized 更可控
- ThreadLocal:为每个线程提供独立副本,彻底避免共享,适用于上下文传递、数据库连接等










