Java 中,锁(Lock)是用于控制多线程并发访问共享资源的工具。它可以确保在同一时刻只有一个线程能够访问共享资源,从而避免数据不一致的问题。
1. 内置锁(synchronized 关键字)
Java 提供了内置锁,也称为监视器锁,这是最简单和常用的锁机制。
- 同步方法:锁住整个方法,锁的对象是
this
(实例锁)或类对象(类锁)。public synchronized void exampleMethod() {// 临界区代码 }
- 同步代码块:锁定指定对象,粒度更细。
public void exampleMethod() {synchronized (this) {// 临界区代码} }
特点
- 自动获取和释放锁。
- 简单易用,但可能导致线程阻塞。
- 不支持尝试获取锁、超时获取锁或中断锁等高级功能。
2. 显式锁(java.util.concurrent.locks.Lock 接口)
显式锁提供了更灵活的锁定机制,允许显式获取和释放锁。
主要实现类
-
ReentrantLock(可重入锁)
- 支持公平锁和非公平锁。
- 允许尝试获取锁、超时获取锁或中断锁。
Lock lock = new ReentrantLock(); try {lock.lock(); // 获取锁// 临界区代码 } finally {lock.unlock(); // 确保释放锁 }
-
ReentrantReadWriteLock(读写锁)
- 提供读锁(共享锁)和写锁(独占锁),适合读多写少的场景。
- 读线程可以并发,写线程则独占。
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.readLock().lock(); // 读操作 lock.writeLock().lock(); // 写操作
特点
- 更灵活,支持多种高级功能。
- 使用时需要手动管理锁的获取和释放,容易引发死锁。
- 性能可能优于
synchronized
,尤其是在高并发情况下。
3. StampedLock
-
StampedLock
是一种改进的读写锁,提供了乐观读锁(Optimistic Read),适合读多写少的场景。 -
与
ReentrantReadWriteLock
不同,StampedLock
不是可重入的。StampedLock lock = new StampedLock(); long stamp = lock.readLock(); try {// 读操作 } finally {lock.unlockRead(stamp); }
4. 信号量(Semaphore)
-
Semaphore
用于控制同时访问某个资源的线程数量。 -
常用于限制连接数或资源池的并发访问。
Semaphore semaphore = new Semaphore(3); // 最大并发数为3 try {semaphore.acquire(); // 获取许可// 临界区代码 } finally {semaphore.release(); // 释放许可 }
5. 偏向锁、轻量级锁和重量级锁
- Java 虚拟机(JVM)底层对
synchronized
的实现进行了优化,包括偏向锁、轻量级锁和重量级锁。 - 这些锁的选择由 JVM 根据竞争情况动态决定,以提高性能。
比较与选择
特性 | synchronized | ReentrantLock | StampedLock |
---|---|---|---|
使用难度 | 简单 | 较复杂 | 较复杂 |
性能 | 较低 | 较高 | 较高 |
可重入性 | 是 | 是 | 否 |
读写锁支持 | 无 | 支持(需额外类) | 支持 |
手动管理锁 | 否 | 是 | 是 |