Java Lock
🧩 一、什么是“锁”?
锁(Lock)是多线程并发编程中用来控制共享资源访问的机制。
核心目标: 防止多个线程同时修改同一个数据,导致数据不一致(线程安全问题)。
🎯 举个生活例子:
假设你和别人共用一个文件夹(共享资源),
- 没有锁:你们同时修改文件 → 文件损坏
- 加锁:一个人编辑时上锁,别人必须等你完成再操作
这就是“锁”的直观意义。
⚙️ 二、Java 中锁的几种体现形式
Java 从语法层面到类库层面,有多种锁机制:
层级 | 锁机制 | 说明 |
---|---|---|
语法级 | synchronized | 最常用关键字,隐式锁 |
API级 | java.util.concurrent.locks.Lock | 显式锁(如 ReentrantLock ) |
原子类 | java.util.concurrent.atomic.* | 基于 CAS 的无锁机制(乐观锁思想) |
我们分别看看。
🧱 1️⃣ synchronized
—— 内置锁(Monitor)
synchronized
是最经典的同步机制。 用法简单,例如:
synchronized (obj) {
// 临界区
}
它底层依赖的是 对象头中的 Monitor 锁(监视器锁), 属于 “悲观锁”(下面会解释)。
在方法上也可以加:
public synchronized void add() { ... }
Java 编译器在字节码中会生成 monitorenter
和 monitorexit
指令。
⚙️ 2️⃣ Lock
接口及其实现类(显式锁)
JDK 1.5 引入了 java.util.concurrent.locks.Lock
接口,更灵活:
Lock lock = new ReentrantLock();
lock.lock();
try {
// 临界区
} finally {
lock.unlock();
}
优点:
- 可以尝试获取锁(
tryLock()
) - 可中断获取锁(
lockInterruptibly()
) - 可以实现公平锁 / 非公平锁
这种锁在功能上比 synchronized
更强,底层通常也基于 AQS
(AbstractQueuedSynchronizer)。
💡 3️⃣ CAS(Compare-And-Swap)与原子类
CAS 是无锁机制(lock-free),它是“乐观锁”的基础。
比如:
AtomicInteger i = new AtomicInteger(0);
i.incrementAndGet(); // 内部用CAS实现
CAS 是一种硬件级原子指令, 会比较“内存中旧值”和“期望值”,如果相等才更新,否则重试。
🧠 三、锁的分类(重要!!)
在理解 Java 锁机制时,我们经常听到:
悲观锁 / 乐观锁 公平锁 / 非公平锁 可重入锁 / 不可重入锁 读写锁 / 独占锁 自旋锁 等
我们来一一梳理清楚:
🔒 1️⃣ 悲观锁(Pessimistic Lock)
- 认为“竞争一定会发生”,所以先上锁再操作。
- 典型代表:
synchronized
,ReentrantLock
示例:
synchronized (this) {
count++;
}
执行时会让其他线程等待锁释放。 特点是:安全但可能慢。
😊 2️⃣ 乐观锁(Optimistic Lock)
- 认为“冲突是少数情况”,所以不加锁,先操作再检查。
- 如果发现冲突,再重试。
实现方式:CAS(Compare-And-Swap)
示例(简化):
while (true) {
int oldValue = atomicInt.get();
int newValue = oldValue + 1;
if (atomicInt.compareAndSet(oldValue, newValue)) {
break; // 成功
}
// 否则重试
}
优点:高性能(无锁) 缺点:竞争激烈时重试多,CPU 占用高。
🧭 3️⃣ 公平锁 vs 非公平锁
- 公平锁:按照线程申请锁的顺序获取锁。
- 非公平锁:允许“插队”,效率更高。
ReentrantLock
默认是非公平的,但可以指定:
new ReentrantLock(true); // 公平锁
🔁 4️⃣ 可重入锁(Reentrant Lock)
同一个线程多次获取同一把锁不会死锁。 例如:
public synchronized void a() {
b(); // 再次进入同一锁
}
public synchronized void b() { }
synchronized
与 ReentrantLock
都是可重入锁。
📚 5️⃣ 读写锁(ReadWriteLock)
当读操作远多于写操作时,为了提高并发度:
- 多个线程可以同时读(不冲突)
- 写时独占
ReadWriteLock rwLock = new ReentrantReadWriteLock();
rwLock.readLock().lock();
...
rwLock.writeLock().lock();
🔁 6️⃣ 自旋锁(Spin Lock)
线程在等待锁时,不挂起,而是循环检查锁状态。 在竞争不激烈的情况下可以减少上下文切换。
Java 的 CAS
实现就体现了这种“自旋”思想。
📊 四、这些锁的关系与演化(总结图)
锁的层级:
┌─────────────────────────────┐
│ synchronized (悲观锁,内置锁) │
│ ReentrantLock (显式锁,可重入) │
│ ReadWriteLock (细粒度锁) │
│ │
│ → AQS (底层实现框架) │
│ → CAS (底层硬件指令) │
└─────────────────────────────┘
↑
└─ 乐观锁思想(基于CAS)
⚡ 五、总结一句话记忆:
类型 | 特点 | 代表实现 |
---|---|---|
悲观锁 | 先锁后干,防冲突 | synchronized, ReentrantLock |
乐观锁 | 干完再比对,冲突重试 | AtomicInteger, CAS |
公平锁 | 排队执行 | ReentrantLock(true) |
非公平锁 | 插队执行 | ReentrantLock(false) |
可重入锁 | 同一线程可重复加锁 | synchronized, ReentrantLock |
读写锁 | 读并发、写独占 | ReentrantReadWriteLock |