Java 线程锁(synchronized、Lock、读写锁)
「线程锁」听起来笼统,面试官往往要你对比 synchronized 和 Lock,再说说公平锁、可中断、读写锁用在什么业务。按这个结构记比较方便。
题目
线程锁有哪些?区别和使用场景?
参考答案(口述要点)
- synchronized:JVM 内置,自动加解锁,可锁方法或代码块,非公平,不可中断等待(早期)
- ReentrantLock:API 锁,可
tryLock、可中断、可选公平,需finally unlock- 读写锁:读多写少时提高并发(
ReentrantReadWriteLock/StampedLock)- 分布式锁:跨 JVM 用 Redis/ZooKeeper,本地锁解决不了
详细解析
synchronized
public class Counter {
private int count;
public synchronized void inc() { // 锁 this
count++;
}
public void dec() {
synchronized (this) { // 锁代码块
count--;
}
}
}JDK6 以后有锁升级:偏向锁 → 轻量级 → 重量级,面试提一句即可。
ReentrantLock
private final ReentrantLock lock = new ReentrantLock(true); // true=公平锁,吞吐略低
public void update() {
lock.lock();
try {
// 临界区
} finally {
lock.unlock(); // 必须 finally,否则异常时锁泄漏
}
}| 对比项 | synchronized | ReentrantLock |
|---|---|---|
| 实现 | JVM 监视器 | AQS |
| 尝试/超时 | 不支持 | tryLock(time) |
| 公平 | 非公平 | 可选公平 |
| 条件队列 | wait/notify | 多个 Condition |
读写锁(缓存、配置热加载)
ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
public String readConfig() {
rw.readLock().lock();
try {
return cache;
} finally {
rw.readLock().unlock();
}
}
public void reload() {
rw.writeLock().lock();
try {
cache = loadFromDb();
} finally {
rw.writeLock().unlock();
}
}写锁独占,读锁共享;写锁会阻塞后续读,适合读远多于写。
代码与实践
- 库存扣减:高并发下 synchronized 锁整个方法容易成为瓶颈,常用 Redis 预减 + DB 乐观锁(
update stock set n=n-1 where id=? and n>0)。 - 单机幂等:
ConcurrentHashMap.putIfAbsent或本地锁保护短临界区。 - 别在锁里调 RPC:持有锁等 HTTP 响应,会把线程池拖死。
写在最后
实际选型:能无锁就无锁;必须互斥优先 synchronized 简单场景;要超时/公平/多条件用 ReentrantLock;跨服务别只聊本地锁,接上 Redis 分布式锁。
创作不易,转载请注明出处和作者。
