synchronized通过“排队执行”实现线程安全:同步代码块精准锁定指定对象的临界区,同步方法隐式锁this或Class对象;二者均依赖JVM Monitor机制确保互斥、可见性与有序性。

理解 synchronized 同步代码块和同步方法,关键在于抓住一个核心:**让多线程对共享资源的访问变成“排队执行”,而不是“一拥而上”**。它不改变程序逻辑,而是加了一道“门禁”——同一时刻只放一个线程进去操作关键代码。
同步代码块:精准控制,锁谁用谁
同步代码块用 synchronized(锁对象) { ... } 包裹真正需要保护的代码段。它的特点是“小而准”:
- 只锁定可能引发冲突的那几行(比如读取、判断、修改共享变量),不影响其他无关逻辑执行
- 锁对象可以是任意对象,但必须保证多个线程使用的是同一个实例——例如在
Runnable实现类中常用this,或显式声明一个private final Object lock = new Object(); - 如果锁对象不同(比如每个线程都 new 了一个新对象),就等于没锁,线程安全问题照旧
同步方法:简单直接,隐式锁 this 或 class
在方法声明前加 synchronized,整个方法体自动成为同步区域:
- 实例方法加
synchronized→ 锁的是当前对象(this),适合多个线程操作同一个对象实例的场景 - 静态方法加
synchronized→ 锁的是当前类的Class对象(如MyClass.class),适用于保护类级别的共享资源(比如静态计数器) - 注意:同步方法粒度比同步代码块大,哪怕方法里只有 1 行涉及共享数据,整段都会被串行化,可能影响性能
它们为什么能解决线程安全问题
根本原因在于 JVM 的互斥锁机制:
- 当线程 A 进入 synchronized 区域时,它会尝试获取指定锁对象的监视器(monitor)
- 若锁空闲,A 拿到锁并执行;若锁已被线程 B 占用,A 就阻塞等待,直到 B 执行完并释放锁
- 锁的释放是自动的:无论正常结束还是抛出异常,JVM 都会确保锁被释放,避免死锁风险
- 这种“一次一人”的约束,彻底切断了多个线程同时读写同一变量导致的中间态错乱(比如卖票时刚判断
ticket > 0,还没减 1 就被另一个线程插进来)
实际使用要注意什么
不是加了 synchronized 就万事大吉,几个常见坑得避开:
- 锁对象必须唯一且共享——用局部变量、每次 new 的对象当锁,等于形同虚设
- 避免嵌套同步或跨方法持锁,容易引发死锁(比如 A 持锁1等锁2,B 持锁2等锁1)
- 同步范围不宜过大——能用同步代码块就别用同步方法,减少不必要的等待
- 它解决的是“并发修改”问题,但不解决“业务逻辑合理性”问题(比如余额为负仍允许扣款,那是校验逻辑缺失,不是线程安全问题)










