1211 字
6 分钟
锁总结
自旋锁(Spin Lock)
- 线程在尝试获取锁时,若锁已被占用,不会立即阻塞,而是通过循环(自旋)不断检查锁状态,直到成功获取锁。
- 适用场景:锁竞争时间短、线程切换开销较高的场景(如高并发短任务)。
import java.util.concurrent.atomic.AtomicBoolean;
public class SpinLock {
private final AtomicBoolean lock = new AtomicBoolean(false);
// 获取锁
public void lock() {
while (!lock.compareAndSet(false, true)) {
// 自旋等待(可插入Thread.yield()减少CPU占用)
}
}
// 释放锁
public void unlock() {
lock.set(false);
}
}
- 优点:无上下文切换开销,响应速度快。
- 缺点:长时间自旋会占用CPU资源,导致“忙等待”。
互斥锁(Mutex Lock)
- 线程在尝试获取锁时,若锁已被占用,会主动阻塞并让出CPU,由操作系统调度其他线程运行。
- 适用场景:锁竞争时间长、线程切换开销较低的场景(如I/O操作、复杂计算)。
// 使用 synchronized
public class SynchronizedMutex {
public synchronized void criticalSection() {
// 临界区代码
}
}
// 使用 ReentrantLock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockMutex {
private final Lock lock = new ReentrantLock();
public void criticalSection() {
lock.lock();
try {
// 临界区代码
} finally {
lock.unlock();
}
}
}
- 优点:不占用CPU资源,适合长时间等待。
- 缺点:上下文切换开销较大,响应延迟较高。
XS锁(eXclusive-Shared Lock)
XS锁是一种由排他锁(Exclusive Lock, X锁)和共享锁(Shared Lock, S锁)组成的锁机制,常见于数据库系统(如MySQL InnoDB的行级锁)和文件系统中。
工作模式
- X锁(排他锁):用于写操作,同一时刻仅允许一个事务/线程持有锁。持有X锁时,其他事务/线程无法获取任何类型的锁(包括X锁和S锁)。
- S锁(共享锁):用于读操作,允许多个事务/线程同时持有S锁。持有S锁时,其他事务/线程仍可获取S锁,但无法获取X锁。
应用场景
- 数据库事务的并发控制(如行级锁、表级锁)。
- 文件系统的读写同步(例如多个进程读取同一文件时加共享锁,写入时加排他锁)。
读写锁(Read-Write Lock)
读写锁是多线程编程中的一种同步机制,分为读锁(Read Lock)和写锁(Write Lock),与XS锁的共享-排他模式高度相似,但更侧重于线程级资源同步。
工作模式
- 读锁(共享锁):允许多个线程同时获取读锁,适用于只读操作。
- 写锁(排他锁):同一时刻仅允许一个线程获取写锁,且获取写锁时不允许其他线程持有读锁或写锁。
应用场景
- 多线程编程中保护共享数据结构(如Java的
ReentrantReadWriteLock
、C++的std::shared_mutex
)。 - 缓存系统的读写优化(高频读取、低频写入场景)。
XS锁 vs 读写锁
- 相同点:均基于“共享读、排他写”的设计,通过区分读/写操作提升并发性能。
- 不同点:
- XS锁是数据库/文件系统层的锁机制,服务于事务和数据一致性;
- 读写锁是线程级的同步工具,用于优化多线程程序的资源访问效率。
在实际应用中,两者可以视为同一思想在不同层级(数据库 vs. 线程)的体现。理解其异同有助于在开发中合理选择锁机制,平衡并发性能与数据一致性。
控制锁粒度的策略
拆分锁(Lock Splitting)
将一个大锁拆分为多个小锁,减少竞争。
// 错误示例:整个对象加锁(粗粒度)
public class Account {
private int balance;
public synchronized void transfer(Account target, int amount) {
// 操作余额
}
}
// 正确示例:拆分锁(细粒度)
public class Account {
private final Object lock = new Object(); // 每个账户独立锁
private int balance;
public void transfer(Account target, int amount) {
// 按固定顺序获取锁(避免死锁)
Object firstLock = System.identityHashCode(this) < System.identityHashCode(target)
? this.lock : target.lock;
Object secondLock = firstLock == this.lock ? target.lock : this.lock;
synchronized (firstLock) {
synchronized (secondLock) {
this.balance -= amount;
target.balance += amount;
}
}
}
}
分段锁(Striping)
将数据分块,每块独立加锁(如 ConcurrentHashMap
的实现)。
public class StripedCounter {
private static final int N_LOCKS = 16;
private final Object[] locks = new Object[N_LOCKS];
private final int[] counts = new int[N_LOCKS];
public StripedCounter() {
for (int i = 0; i < N_LOCKS; i++) {
locks[i] = new Object();
}
}
public void increment(int key) {
int stripe = key % N_LOCKS;
synchronized (locks[stripe]) {
counts[stripe]++;
}
}
}
读写锁(ReadWriteLock)
区分读锁(共享)和写锁(排他),提升读多写少场景的性能。
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteCache {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Map<String, Object> cache = new HashMap<>();
public Object get(String key) {
rwLock.readLock().lock();
try {
return cache.get(key);
} finally {
rwLock.readLock().unlock();
}
}
public void put(String key, Object value) {
rwLock.writeLock().lock();
try {
cache.put(key, value);
} finally {
rwLock.writeLock().unlock();
}
}
}
无锁编程(Lock-Free)
使用原子类(AtomicXXX
)或CAS操作避免锁。
import java.util.concurrent.atomic.AtomicInteger;
public class LockFreeCounter {
private final AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // 无锁操作
}
}