重入锁定义
如类名可知,锁的类型是可重入锁,是目前JAVA中使用较为广泛的锁,也就是说当前持有锁的线程再次获取不会被阻塞。ReentrantLock支持两种锁:公平锁和非公平锁。如果锁是公平锁那么获取顺序就应该符合请求上的绝对时间顺序,满足FIFO。
ReentrantLock解决如下两个问题:
1.在线程获取锁的时候;如果已经获取锁的线程是当前线程的话则直接再次获取成功;
2.由于锁会被获取n次;那么只有锁在被释放同样的n次之后,该锁才算是完全释放成功。
//可重入锁ReentrantLock,通过其内部实现AbstractQueuedSynchronizer的Sync类来完成锁的功能
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
//是否生成公平锁实例,默认情况下生成NonfairSync非公平锁.
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
}
线程加锁(独占)
独占模式加锁分为公平锁与非公平锁,具体加锁的流程分为如下几个步骤:
1,程序通过lock为入口,通过acquire来获取线程锁,这里调用公平锁与非公平锁各自实现中的tryAcquire来实现。
2,当tryAcquire返回true表示抢占锁成功.否则执行如下流程:
3,线程抢占锁失败(addWaiter),先生成一个独占模式的node节点,并把节点添加到队列尾部.
4,通过acquireQueued不断的轮询等待队列,尝试抢占线程锁(其实这里在轮询一次抢占失败后会BLOCKED线程,等待线程的前继节点唤醒后在重新进行抢占).
下面分析下非公平锁与公平锁的实现.
ReentrantLock只实现了Look的独占模式加锁。
非公平锁
ReentrantLock的非公平锁由其内部类”NonFairSync”实现,包含两种情况:
- 如果线程来抢占锁资源时,锁没有被其它线程持有,直接设置锁的数量为1,并设置锁的持有线程为当前线程。
- 如果锁已经被线程持有,通过AQS.acquire函数尝试线程锁的重入,尝试重入线程锁时非公平锁的实现由Sync.nonfairTryAcquire函数实现。
实现流程如下所示:
//ReentrantLock独占模式下非公平锁的加锁流程.
static final class NonfairSync extends Sync {
//a,ReentrantLock非公平锁的加锁实现
final void lock() {
//1,如果是首次加锁,直接设置当前ReentrantLock的所有者为当前线程,并设置锁的数量为1
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
} else {
//2,这里是线程锁重入,调用AbstractQueuedSynchronizer.acquire来完成加锁.