介绍
- 它是可重入锁的互斥锁,又被称为“独占锁”。
- 它在同一时间点只能被一个线程锁持有;可重入表示,ReentrantLock锁可被同一个线程多次获取。
- 它是通过一个FIFO的等待队列来管理获取该锁所有线程的。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。
ReentrantLock构造方法
/**
* 默认创建非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* fair为true表示是公平锁,
fair为false表示是非公平锁
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
主要方法
获取和释放锁
lock()
:
- 阻塞式地获取锁。如果锁已经被其他线程持有,当前线程将被阻塞,直到获取到锁。
tryLock()
:
- 非阻塞式地尝试获取锁。成功获取锁返回
true
,失败返回false
。
lockInterruptibly()
:
- 类似于
lock()
,但支持对获取锁的过程中被中断。
tryLock(long timeout, TimeUnit unit)
:
- 在指定的时间范围内尝试获取锁,超时后返回结果。
unlock()
:
- 释放锁。必须在当前线程持有锁的情况下调用,否则会抛出
IllegalMonitorStateException
。
查询锁状态
isHeldByCurrentThread()
:
- 查询当前线程是否持有锁。
getHoldCount()
:
- 查询当前线程持有锁的次数。
getQueueLength()
:
- 查询正在等待获取锁的线程数。
hasQueuedThreads()
:
- 查询是否有线程在等待获取锁。
其他方法
isFair()
:
- 查询锁是否是公平锁。
isLocked()
:
- 查询锁是否被任意线程持有。
newCondition()
:
- 返回与此锁关联的新
Condition
实例,用于实现更灵活的线程间通信。
示例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Example {
private Lock lock = new ReentrantLock();
public void performTask() {
lock.lock(); // 获取锁
try {
// 执行需要同步的任务
} finally {
lock.unlock(); // 释放锁
}
}
}
在使用 ReentrantLock
时,通常将 lock
和 unlock
放在 try-finally
块中,以确保在发生异常时也能正确释放锁。
源码分析
lock方法分析
以下是lock方法的大体逻辑uml流程图:
以下是代码调用顺序流程图:
tryLock()方法分析
ReentrantLock tryLock方法是一个可重入的互斥锁,它用于尝试获取独占锁,如果锁已经被其他线程持有,那么当前线程会立即返回false,不会进入等待队列。它的基本逻辑是:
- 调用nonfairTryAcquire方法,传入参数1,表示获取一个锁。
- 调用tryAcquire方法,尝试通过CAS操作更新state变量,表示锁的状态和重入次数。
- 如果state为0,说明锁未被占用,那么尝试将state设置为1,如果成功则设置当前线程为独占锁的线程,并返回true。
- 如果state不为0,说明锁已经被占用,那么判断持有锁的线程是否为当前线程,如果是则累加state值,表示重入锁的次数,并返回true。
- 如果以上两种情况都不满足,那么返回false,表示获取锁失败。
如下图所示:
时序流程图,如下所示:
unlock()方法分析
ReentrantLock unlock方法是一个可重入的互斥锁,它用于释放独占锁,如果锁已经被当前线程重入多次,那么只有当state变为0时,才会真正释放锁,并唤醒等待队列中的下一个线程。它的基本逻辑是:
- 调用sync.release方法,传入参数1,表示释放一个锁。
- 调用tryRelease方法,判断当前线程是否为独占锁的线程,如果不是则抛出IllegalMonitorStateException异常。
- 如果是,则减少state值,表示减少重入锁的次数。
- 如果state为0,说明锁已经完全释放,那么清空独占锁的线程,并返回true。
- 如果state不为0,说明锁还未完全释放,那么返回false。
如下UML流程图所示:
下面的是时序图:
lockInterruptibly()方法分析
ReentrantLock lockInterruptibly方法是一个可重入的互斥锁,它用于获取独占锁,如果锁已经被其他线程持有,那么当前线程会进入等待队列,直到获取到锁或者被中断。它的基本逻辑是:
- 调用acquireInterruptibly方法,传入参数1,表示获取一个锁。
- 调用tryAcquire方法,尝试通过CAS操作更新state变量,表示锁的状态和重入次数。
- 如果state为0,说明锁未被占用,那么尝试将state设置为1,如果成功则设置当前线程为独占锁的线程,并返回true。
- 如果state不为0,说明锁已经被占用,那么判断持有锁的线程是否为当前线程,如果是则累加state值,表示重入锁的次数,并返回true。
- 如果以上两种情况都不满足,那么返回false,表示获取锁失败。
- 如果tryAcquire方法返回false,那么调用addWaiter方法,将当前线程封装为一个节点,加入到等待队列的尾部。
- 然后调用acquireQueued方法,使当前线程在等待队列中自旋,直到获取到锁或者被中断。
我为您生成了一个UML流程图,如下所示:
时序图如下:
tryLock(long timeout, TimeUnit unit)
分析
ReentrantLock tryLock(long timeout, TimeUnit unit)方法是一个可重入的互斥锁,它用于尝试获取独占锁,如果锁已经被其他线程持有,那么当前线程会在指定的时间内等待,如果在期间获取到锁,就返回true,否则返回false。它的基本逻辑是:
- 调用tryAcquireNanos方法,传入参数1和单位转换后的纳秒数,表示获取一个锁并设置超时时间。
- 调用tryAcquire方法,尝试通过CAS操作更新state变量,表示锁的状态和重入次数。
- 如果state为0,说明锁未被占用,那么尝试将state设置为1,如果成功则设置当前线程为独占锁的线程,并返回true。
- 如果state不为0,说明锁已经被占用,那么判断持有锁的线程是否为当前线程,如果是则累加state值,表示重入锁的次数,并返回true。
- 如果以上两种情况都不满足,那么返回false,表示获取锁失败。
- 如果tryAcquire方法返回false,那么调用addWaiter方法,将当前线程封装为一个节点,加入到等待队列的尾部。
- 然后调用doAcquireNanos方法,使当前线程在等待队列中自旋,直到获取到锁或者被中断或者超时。
如下所示:
时序图如下所示:
总结
通过源码流程分析让我们更加深入了解了ReentrantLock锁的实现流程。