并发场景下为什么推荐ReentrantLock而不是Synchronized
2024-02-09 01:07:03
引言
在多线程编程中,锁是协调线程访问共享资源的至关重要的工具。Java提供了两种常用的锁机制:Synchronized
和ReentrantLock
。虽然Synchronized
是一种内置锁,使用起来更加方便,但ReentrantLock
却是一种更加灵活、可定制的锁,在高并发场景下具有显著的优势。
ReentrantLock与Synchronized的比较
1. 可重入性
顾名思义,ReentrantLock
支持可重入,即同一个线程可以多次获取同一把锁。而Synchronized
只允许线程一次获取锁,如果线程已经持有锁,再次尝试获取时会发生死锁。在高并发场景下,可重入性至关重要,因为它允许线程在获取锁后再次获取同一把锁,避免死锁。
2. 等待队列
ReentrantLock
具有等待队列,当一个线程试图获取锁时,如果锁被另一个线程持有,该线程会被阻塞并添加到等待队列中。当锁释放时,等待队列中的第一个线程将获得锁。而Synchronized
没有等待队列,如果锁被另一个线程持有,试图获取锁的线程会直接被挂起,这可能会导致线程饥饿。
3. 公平性和非公平性
ReentrantLock
支持公平锁和非公平锁两种模式。公平锁保证按线程获取锁的顺序进行排队,而非公平锁则允许线程跳过等待队列直接获取锁。在高并发场景下,公平锁可以防止线程饥饿,但可能会降低吞吐量。Synchronized
始终使用非公平锁。
4. 自定义锁
ReentrantLock
是一个可定制的锁,允许开发者根据需要创建不同类型的锁。例如,开发者可以创建定时锁、条件锁或读写锁。而Synchronized
是一个内置锁,不可定制。
示例
以下是一个简单的示例,展示了ReentrantLock
在高并发场景下的优势:
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count;
private ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
在这个示例中,Counter
类使用ReentrantLock
保护共享变量count
。当多个线程同时调用increment
方法时,ReentrantLock
可确保只有一个线程可以同时修改count
,从而避免并发问题。
结论
综上所述,在高并发场景下,ReentrantLock
由于其可重入性、等待队列、可定制性和公平锁支持等优势,而被推荐为比Synchronized
更好的锁机制。它提供了更大的灵活性、可扩展性和对并发问题的更佳处理。