synchronized内置锁
synchronized 内置锁
synchronized 使用对象监视器实现互斥与内存可见性。竞争同一把锁的线程,同一时刻最多一个进入临界区。
三种写法与锁对象
同步代码块锁住括号中的对象:
代码块收起展开
private final Object lock = new Object();
void increment() {
synchronized (lock) {
count++;
}
}实例同步方法锁住当前实例 this,不是锁住该类的所有对象:
代码块收起展开
public synchronized void increment() {
count++;
}静态同步方法锁住 CurrentClass.class。它和实例同步方法不是同一把锁:
代码块收起展开
public static synchronized void reset() {
count = 0;
}保证
互斥与复合操作原子性
临界区操作相对于使用同一把锁的其他线程不可交错。所有相关访问都遵守同一锁规则,数据竞争才真正消除。
可见性
同一监视器的解锁 happens-before 后续加锁。解锁前的写入,对随后获取同一把锁的线程可见。
有序性
JVM 仍可进行不破坏语义的优化和重排,但不能让正确同步的线程观察到违反 happens-before 的结果。不能简单理解为“临界区内完全不重排”。
可重入
同一线程已经持有某对象锁时,可以再次获取该锁。JVM 会记录持有线程和重入次数。
wait / notify
wait、notify、notifyAll 必须在持有对应监视器时调用。
代码块收起展开
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 | 是 | 是 | 超时、可中断、公平锁、多条件队列 |