ReentrantLock-可重入锁
ReentrantLock是Java并发包java.util.concurrent.locks中的一个类,它实现了Lock接口,提供了一种与Synchronized方法和语句相同的基本行为和语义的互斥锁,但具有更多的扩展功能。
主要特点
- 可重入性
与 synchronized 关键字一样,ReentrantLock 允许同一个线程多次获得锁,而不会发生死锁。每次成功获取锁后,锁的持有计数会增加,每次释放锁时,计数会减少。只有当计数为0时,锁才完全释放,其他线程才能获取该锁。 - 公平性
ReentrantLock 构造函数接受一个可选的 boolean 类型的 fairness 参数。如果设置为 true,则锁将按照请求锁的顺序(即等待时间最长的线程将优先获得锁),来授予访问权限。这有助于减少饥饿现象,但可能会降低吞吐量。如果设置为 false(默认值),则不保证任何特定的访问顺序。 - 尝试非阻塞地获取锁
ReentrantLock 提供了 tryLock() 方法,该方法尝试获取锁,如果锁可用,则立即返回 true,否则立即返回 false。这与 synchronized 关键字不同,后者在锁不可用时会使线程阻塞。 - 可中断的锁获取
ReentrantLock 支持可中断的锁获取操作,即线程在等待锁的过程中可以被中断。这通过 lockInterruptibly() 方法实现,如果当前线程在等待锁的过程中被中断,则会抛出 InterruptedException。 - 条件变量
与 synchronized 关键字不同,ReentrantLock 提供了条件变量(Condition),允许多个条件等待集。这允许更灵活的线程同步控制。
使用建议
- 防止死锁
使用try-finally语句,在调用lock()方法后,应立即使用try-finally语句来确保锁最终会被释放; - 考虑公平性
如果实际应用场景中,锁的获取顺序很重要,或者希望减少饥饿现象,可以考虑将fairness参数设置为true。但请注意,这可能会降低系统的吞吐量。 - 避免过度使用
虽然 ReentrantLock提供了比synchronized更多的功能,但过度使用可能会使代码变得复杂且难以维护。在大多数情况下,synchronized 关键字已经足够使用。
示例代码
class ReentrantLockSample {
// 默认非公平锁
private final ReentrantLock lock = new ReentrantLock();
// 公平锁
// private final ReentrantLock lock = new ReentrantLock(true);
public void method() {
// 尝试获取锁
lock.lock();
try {
// ... 方法体
} finally {
// 释放锁
lock.unlock();
}
}
}
在这个例子中,lock.lock() 尝试获取锁,如果锁被其他线程持有,则当前线程将阻塞,直到锁被释放。在 try 块中执行的方法体完成后,无论是否发生异常,finally 块都会确保锁被释放。
获取锁的过程
获取锁有两种方式:lock()或者tryLock()。lock()方法会根据获取锁策略(公平与否)去获取锁;tryLock()则是想要立即获取锁,所以无论是哪种获取锁策略都是通过非公平方式获取锁。
- 公平锁(先到先得)
获取锁的state状态,如果为0则尝试获取锁;如果已有其他线程获取到锁则加入到队尾等待;
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 1.首先查询是否有比当前线程获取锁等待时间更长的线程
// 2.更新状态-获取成功
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 重入锁获取
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
// 最大重入次数为int的最大值
if (nextc < 0){
throw new Error("Maximum lock count exceeded");
}
setState(nextc);
return true;
}
return false;
}
- 非公平锁(随机)
直接尝试获取锁,获取成功则将当前线程设置独占;获取失败则通过公平锁获取方式重新获取;
final void lock() {
if (compareAndSetState(0, 1)){
setExclusiveOwnerThread(Thread.currentThread());
}else{
acquire(1);
}
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 更新状态-获取成功
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 重入锁获取
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
// 最大重入次数为int的最大值
if (nextc < 0){
throw new Error("Maximum lock count exceeded");
}
setState(nextc);
return true;
}
return false;
}