Java 并发编程深度解析:锁机制与 Guava Monitor
一、锁的本质与作用
1. 锁是什么?
锁是并发编程中的同步机制,用于控制多个线程对共享资源的访问。可以理解为资源访问的"许可证"机制 - 只有获得锁的线程才能访问共享资源。
2. 锁的核心作用
作用 |
说明 |
示例场景 |
互斥访问 |
确保同一时间只有一个线程访问共享资源 |
银行账户余额修改 |
可见性保证 |
确保一个线程的修改对其他线程可见 |
缓存数据更新 |
有序性控制 |
确保操作按预期顺序执行 |
数据库事务操作 |
3. 锁与并发/异步编程的关系
- 并发编程:需要锁来解决共享资源竞争问题
- 异步编程:通过非阻塞模型减少锁的使用,但底层资源访问仍可能需要锁
- 最佳实践:在共享资源访问点使用锁,在非阻塞操作中使用异步机制
二、ReentrantLock 详解
1. ReentrantLock 核心特性
- 可重入性:同一线程可多次获取同一把锁
- 公平性选择:支持公平锁与非公平锁
- 条件变量:可创建多个 Condition 对象
- 锁中断:支持响应中断的锁获取
- 尝试获取:提供 tryLock() 方法
2. 完整使用示例
import java.util.concurrent.locks.*;
public class ReentrantLockDemo {
private final ReentrantLock lock = new ReentrantLock();
private int counter = 0;
public void increment() {
lock.lock();
try {
counter++;
System.out.println(Thread.currentThread().getName() +
" incremented to: " + counter);
} finally {
lock.unlock();
}
}
public void conditionalUpdate() throws InterruptedException {
Condition condition = lock.newCondition();
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
while (counter < 5) {
System.out.println("Waiting for condition...");
condition.await(500, TimeUnit.MILLISECONDS);
}
counter = 0;
System.out.println("Counter reset to 0");
condition.signalAll();
} finally {
lock.unlock();
}
} else {
System.out.println("Could not acquire lock in time");
}
}
public static void main(String[] args) {
ReentrantLockDemo demo = new ReentrantLockDemo();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
for (int j = 0; j < 3; j++) {
demo.increment();
}
}).start();
}
}
}
3. 关键方法解析
方法 |
说明 |
使用场景 |
lock() |
获取锁,阻塞直到成功 |
必须访问临界区时 |
tryLock() |
尝试获取锁,立即返回结果 |
避免长时间阻塞 |
tryLock(timeout, unit) |
带超时的锁获取 |
避免死锁 |
lockInterruptibly() |
可中断的锁获取 |
需要响应中断的场景 |
newCondition() |
创建条件变量 |
复杂条件等待场景 |
三、ReentrantLock vs synchronized
特性 |
ReentrantLock |
synchronized |
实现机制 |
API 实现 |
JVM 内置关键字 |
锁获取方式 |
lock()/tryLock() |
自动获取和释放 |
公平性 |
支持公平/非公平 |
非公平锁 |
条件变量 |
支持多个 Condition |
单一 wait/notify |
锁中断 |
支持 lockInterruptibly() |
不支持 |
超时控制 |
支持 tryLock(timeout) |
不支持 |
性能 |
高竞争下更优 |
低竞争下更优 |
代码灵活性 |
高(可跨方法) |
低(代码块内) |
异常处理 |
需手动释放锁 |
自动释放锁 |
四、Guava Monitor 深度解析
1. Monitor 是什么?
Monitor 是 Google Guava 库提供的高级锁工具,基于 ReentrantLock 构建,但提供更简洁的 API 和更安全的并发控制模式。
2. Monitor 核心优势
- 条件抽象:通过 Guard 对象封装条件逻辑
- 隐式信号:自动处理线程唤醒,避免信号丢失
- 可读性:代码更清晰表达业务意图
- 安全性:减少常见并发错误
3. Monitor 与 ReentrantLock/synchronized 的区别
特性 |
Monitor |
ReentrantLock |
synchronized |
条件封装 |
Guard 对象 |
Condition 对象 |
无直接支持 |
信号处理 |
全自动 |
手动 signal/signalAll |
手动 notify/notifyAll |
多条件支持 |
多 Guard 支持 |
多 Condition 支持 |
单一条件 |
条件检查 |
自动循环检查 |
需手动 while 循环 |
需手动 while 循环 |
公平性 |
支持 |
支持 |
不支持 |
代码简洁性 |
高 |
中 |
低 |
学习曲线 |
较陡峭 |
中等 |
平缓 |
五、Guava Monitor 完整使用案例
1. 生产者-消费者模型实现
import com.google.common.util.concurrent.Monitor;
import java.util.ArrayList;
import java.util.List;
public class ProducerConsumerMonitor {
private final Monitor monitor = new Monitor();
private final List<Integer> buffer = new ArrayList<>();
private static final int MAX_SIZE = 5;
private final Monitor.Guard notFull = new Monitor.Guard(monitor) {
@Override
public boolean isSatisfied() {
return buffer.size() < MAX_SIZE;
}
};
private final Monitor.Guard notEmpty = new Monitor.Guard(monitor) {
@Override
public boolean isSatisfied() {
return !buffer.isEmpty();
}
};
public void produce(int item) throws InterruptedException {
monitor.enterWhen(notFull);
try {
buffer.add(item);
System.out.println("Produced: " + item + " | Buffer size: " + buffer.size());
} finally {
monitor.leave();
}
}
public int consume() throws InterruptedException {
monitor.enterWhen(notEmpty);
try {
int item = buffer.remove(0);
System.out.println("Consumed: " + item + " | Buffer size: " + buffer.size());
return item;
} finally {
monitor.leave();
}
}
public static void main(String[] args) {
ProducerConsumerMonitor pc = new ProducerConsumerMonitor();
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <=