synchronized内置锁

synchronized 内置锁

synchronized 使用对象监视器实现互斥与内存可见性。竞争同一把锁的线程,同一时刻最多一个进入临界区。

三种写法与锁对象

同步代码块锁住括号中的对象:

代码块JAVA · 7 行收起展开
private final Object lock = new Object();

void increment() {
    synchronized (lock) {
        count++;
    }
}

实例同步方法锁住当前实例 this,不是锁住该类的所有对象:

代码块JAVA · 3 行收起展开
public synchronized void increment() {
    count++;
}

静态同步方法锁住 CurrentClass.class。它和实例同步方法不是同一把锁:

代码块JAVA · 3 行收起展开
public static synchronized void reset() {
    count = 0;
}

保证

互斥与复合操作原子性

临界区操作相对于使用同一把锁的其他线程不可交错。所有相关访问都遵守同一锁规则,数据竞争才真正消除。

可见性

同一监视器的解锁 happens-before 后续加锁。解锁前的写入,对随后获取同一把锁的线程可见。

有序性

JVM 仍可进行不破坏语义的优化和重排,但不能让正确同步的线程观察到违反 happens-before 的结果。不能简单理解为“临界区内完全不重排”。

可重入

同一线程已经持有某对象锁时,可以再次获取该锁。JVM 会记录持有线程和重入次数。

wait / notify

wait、notify、notifyAll 必须在持有对应监视器时调用。

代码块JAVA · 6 行收起展开
synchronized (queue) {
    while (queue.isEmpty()) {
        queue.wait();
    }
    Task task = queue.remove();
}
  • wait() 释放锁并等待。
  • sleep() 不释放已经持有的锁。
  • 使用 while 重新检查条件,处理虚假唤醒。
  • 复杂协作优先使用 BlockingQueue 等高级工具。

锁对象选择

  • 优先使用 private final Object lock。
  • 不要锁字符串字面量、装箱值或公开对象。
  • 不要锁每次新建的对象,那样线程不会竞争。
  • 临界区内避免慢 I/O、网络调用和不可控回调。

死锁与工具选择

多个锁的获取顺序不一致可能死锁。应统一顺序、减少嵌套锁、缩小范围,或使用 ReentrantLock.tryLock()。

工具互斥可见性典型用途
volatile状态标志、独立读写
synchronized保护复合状态
ReentrantLock超时、可中断、公平锁、多条件队列

延伸阅读