synchronized

82 阅读2分钟

synchronized 是 Java 中的一个关键字,是 Java 内置的互斥与内存可见性机制。主要解决多个线程访问资源时的竞争问题。

三种使用方式

  • 修饰实例方法(非静态)

    • 锁对象: 当前实例 this(等价于 synchronized(this))

    • 作用范围: 同一实例上的所有 synchronized 实例方法互斥;不同实例互不影响

    • 适用场景: 保护实例字段(对象状态)

  • 修饰静态方法

    • 锁对象: 类对象 类名.class

    • 作用范围: 所有实例共享一把锁;跨实例也互斥

    • 适用场景: 保护静态字段/全局资源

  • 同步代码块

    • 锁对象: 自定义任意对象 synchronized(锁),粒度可控

    • 作用范围: 仅包裹的临界区

    • 适用场景: 需要缩小临界区提高并发,或按不同资源拆分多把锁

public class Counter {
    private int count;
    private static int global;
    private final Object lock = new Object();

    // 1) 实例方法锁:锁 this
    public synchronized void incA() {
        count++;
    }

    // 2) 静态方法锁:锁 Counter.class
    public static synchronized void incGlobal() {
        global++;
    }

    // 3) 同步代码块:锁自定义对象
    public void incB() {
        synchronized (lock) {
            count++;
        }
    }
}

底层原理

实现方式

编译为字节码指令 monitorenter/monitorexit(方法级同步用 ACC_SYNCHRONIZED 标志)。

public class R {
    public static void main(String[] args) {
        new R().a();
    }

    private synchronized void a() {
        System.out.println(Thread.currentThread().getId() + ": a()");
        b();
    }

    private synchronized void b() {
        System.out.println(Thread.currentThread().getId() + ": b()");
        c();
    }

    private synchronized void c() {
        System.out.println(Thread.currentThread().getId() + ": c()");
    }
}
  • monitorenter 此时计数器为 0,获取锁

    • a() monitor计数器 += 1 = 1
    • b() monitor计数器 += 1 = 2
    • c() monitor计数器+= 1 = 3
  • monitorexit

    • c() 执行结束,monitor计数器 -= 1 = 2
    • b() 执行结束,monitor计数器 -= 1 = 1
    • a() 执行结束,monitor计数器 -= 1 = 0
  • 释放锁

锁升级

  • 对象头(Mark Word)存放锁状态/哈希等元数据,JVM 基于它实现多种锁形态并在运行时转换「锁升级」:

    • 无锁 → 轻量级(自旋/偏向优化)→ 重量级(阻塞,OS 互斥量)

    • 自旋锁与阻塞之间可能采用“自旋-再阻塞”策略以降低上下文切换成本

偏向锁在 JDK 15 起默认关闭,JDK 18 起被移除;现代 JDK 仍保留轻量/重量级与自旋等优化。

与 volatile 的关系

  • volatile 提供可见性与有限的有序性,不提供互斥。synchronized 同时提供“互斥 + 可见性/有序性”
  • volatile 只能作用于变量,synchronized 可以修饰方法以及代码块 。