synchronized通过Monitor机制实现互斥执行,确保临界区原子性:线程必须先获取锁才能执行,未获锁则阻塞,执行完自动释放锁并刷新主内存,使count++等非原子操作成为不可分割的整体。

Java 多线程共享资源时,要确保操作的原子性,核心是让临界区(即读写共享变量的代码段)被**互斥执行**——同一时刻只允许一个线程进入。synchronized 是最直接、最常用的手段,它通过对象监视器(monitor)机制天然支持原子性、可见性和有序性。
同步方法:锁住整个方法,简单但粒度粗
用 synchronized 修饰实例方法时,锁对象是当前实例(this);修饰静态方法时,锁对象是该类的 Class 对象。JVM 保证:线程必须先获得锁,才能执行方法体;方法执行完(包括异常退出)后自动释放锁。这使得方法内所有操作构成一个不可分割的原子单元。
例如:
public synchronized void increment() {
count++; // 这一行看似简单,实际包含“读取→+1→写回”三步,synchronized 把它打包成原子操作
立即学习“Java免费学习笔记(深入)”;
注意:若多个线程操作的是不同对象实例,它们持有的是不同 this 锁,彼此不互斥——所以同步方法只对同一对象有效。
同步代码块:精准控制锁范围,推荐用于性能敏感场景
相比同步方法,同步代码块允许你只锁定真正需要保护的那几行代码,避免无谓阻塞。关键是显式指定一个**锁对象**,只要多个线程竞争同一把锁,就能保证原子性。
常见锁对象选择:
- 用 this:适用于当前对象所有同步块共用一把锁(等价于同步方法)
- 用私有 final Object:如 private final Object lock = new Object();,更安全,避免外部误锁或锁升级风险
- 用 Class 对象:如 synchronized(Counter.class),适用于需要跨实例同步的静态资源
示例:
private final Object lock = new Object();
public void increment() {
synchronized(lock) {
count++; // 只有这一句受保护,其余逻辑可并发执行
}
}
为什么 synchronized 能保证原子性
原子性不是靠“指令不可拆分”实现的(Java 字节码里 count++ 本来就是多条指令),而是靠锁的排他性 + JVM 内存模型保障:
- 线程进入 synchronized 块前,必须获取锁;未获锁则阻塞,不会执行内部代码
- 持有锁的线程执行完后,会将工作内存中修改的共享变量强制刷新到主内存
这就确保了“读-改-写”整个流程在逻辑上不可中断、结果一致——即具备原子语义。
补充提醒:哪些情况不能靠 synchronized 解决
同步机制只管“执行顺序”,不管“业务逻辑”。以下问题需额外设计:
- 复合操作仍需整体加锁:比如“先检查再更新”(if (count
- volatile 不能替代 synchronized:它只保可见性,不保原子性(如 i++ 依然线程不安全)
- 锁对象必须一致:如果一部分代码 synchronized(this),另一部分 synchronized(new Object()),两者毫无互斥关系


















