在 java 编程中,自旋锁(Spin Lock) 和 互斥锁 (Mutex Lock) 都是用来实现线程同步的锁机制,它们有不同的工作原理和使用场景。
自旋锁(Spin Lock)
自旋锁是一种忙等待的锁机制。当一个线程试图获取自旋锁时,如果锁已经被其他线程持有,那么该线程不会立即进入阻塞状态,而是会不断地循环检查锁的状态,直到它能够获取锁为止。这种锁机制适用于锁持有时间非常短的场景,因为自旋锁避免了线程的上下文切换开销,但会消耗 CPU 资源。
示例代码:
import java.util.concurrent.atomic.AtomicBoolean;
public class SpinLock {
private final AtomicBoolean lock = new AtomicBoolean(false);
public void lock() {
while (!lock.compareAndSet(false, true)) {
// 自旋等待
}
}
public void unlock() {
lock.set(false);
}
}
互斥锁(Mutex Lock)
互斥锁是一种阻塞锁机制。当一个线程试图获取互斥锁时,如果锁已经被其他线程持有,那么该线程将会进入阻塞状态,直到锁被释放。互斥锁通常由操作系统实现,能够有效地管理资源并减少CPU的浪费。Java中常用的互斥锁实现是ReentrantLock和synchronized关键字。
示例代码:
使用 synchronized 关键字:
synchronized 是 java 提供的原生关键字,用于在方法或代码块上加锁,以确保同一时间只有一个线程可以执行被同步的代码块。
public class MutexLockExample {
private final Object lock = new Object();
public void criticalSection() {
synchronized (lock) {
// 关键代码区
}
}
}
使用 ReentrantLock:
ReentrantLock 是 java.util.concurrent.locks 包下的一个类,提供了与 synchronized 类似的功能,但它更灵活,具有更强的功能,例如可以实现公平锁、可中断锁等。
import java.util.concurrent.locks.ReentrantLock;
public class MutexLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void criticalSection() {
lock.lock();
try {
// 关键代码区
} finally {
lock.unlock();
}
}
}
区别与应用场景
-
自旋锁:
- 优点:避免了线程的上下文切换开销,适用于锁持有时间极短的场景。
- 缺点:如果锁持有时间较长,会导致CPU资源的浪费。
- 应用场景:高性能计算、锁持有时间极短的并发场景。
-
互斥锁:
- 优点:由操作系统管理,适用于各种场景,能够有效地管理资源。
- 缺点:线程在获取锁失败时会进入阻塞状态,存在上下文切换开销。
- 应用场景:一般的并发编程场景,尤其是锁持有时间不确定或较长的场景。
在实际项目中一般使用哪种锁?
在实际项目编程中,一般情况下使用互斥锁 Mutex Lock 较多,主要是因为其使用简单,适用场景广泛,且安全可靠。
互斥锁的两种主要实现方式也就是上面说的这两种, synchronized 关键字 和 ReentrantLock 类。
对比一下这两种实现方式:
-
synchronized 关键字
- 优点:语法简单,易于使用,自动处理锁的获取和释放;
- 缺点:功能相对较少,例如无法中断线程,无法实现公平锁。
-
ReenTrantLock 类
- 优点:功能强大,提供了更多的控制选项,例如可以实现公平锁、可中断锁、具有 tryLock() 方法 ,可以尝试获取锁而不阻塞;
- 缺点:需要显式的获取和释放锁,代码相对复杂。
对于简单的同步需要求,尤其是在单一锁的情况下,使用 synchronized 关键字通常是最好的选择。
在需要更灵活的锁控制,或需要特殊功能(如公平锁、可中断锁)时,使用 ReentrantLock 会更合适。