JUC-CountDownLatch基础篇
JUC-CountDownLatch源码分析
JUC-Semaphore基础篇
JUC-Semaphore源码分析
JUC-ReentrantReadWriteLock锁基础篇
JUC-ReentrantReadWriteLock锁源码分析
JUC-ReentrantLock锁基础篇
JUC-ReentrantLock锁源码分析
JUC-CyclicBarrier基础篇
JUC-CyclicBarrier源码分析
建议阅读ReentrantLock锁源码时,先去阅读JUC-ReentrantLock锁基础篇,涉及到的AQS源码可以参考JUC-AQS源码篇
文章目录
- 1.ReentrantLock类结构图
-
- 1.1ReentrantLock的构造方法
- 2.lock()方法源码
-
- 2.1 公平锁时抽象方法lock()的子类实现源码
- 2.2 非公平锁时抽象方法lock()的子类实现源码
- 3.unlock()方法源码
1.ReentrantLock类结构图
ReentrantLock内部有一个继承AbstractQueuedSynchronizer的静态抽象类Sync,NonfairSync和FairSync继承了Sync并实现了它的抽象方法lock()。
在ReentrantLock中,AQS有tryAcquire(int arg),tryRelease(int arg)两个方法需要子类来实现。而子类Sync只实现了tryRelease方法,tryAcquire由Sync的子类NonfairSync和FairSync来实现。而Sync自己也有一个抽象方法方法lock()需要它的子类来实现。
总的来说就是
Sync实现了AQS的抽象方法tryRelease。
NonfairSync实现了AQS的抽象方法tryAcquire和Sync的抽象方法lock
FairSync也实现了AQS的抽象方法tryAcquire和Sync的抽象方法lock
1.1ReentrantLock的构造方法
private final Sync sync;
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
由上面可以看出通过参数fair可以控制是创造一个公平的锁ReentrantLock还是一个非公平的锁ReentrantLock。什么是公平锁什么是非公平锁可以参考上面的JUC-ReentrantLock锁基础篇中的公平锁,非公平锁的讲解。
ReentrantLock加锁的方法调用大致过程是:
reentrantLock类的lock()调用—>Sync子类实现的lock()调用—>AQS类的acquire(1)调用—>Sync子类实现的tryAcquire(1)
ReentrantLock解锁的方法调用大致过程是:
reentrantLock类的unlock()调用—>AQS类的release(1)—>Sync类的tryRelease(1)
在ReentrantLock中AQS的成员变量state取值的含义:
- state值等于0,代表这把锁目前没有被其他线程获取。
- state值大于0,代表已经有线程获取到了这把锁。
2.lock()方法源码
我们知道ReentrantLock获取锁调用的入口方法是reentrantLock.lock()方法。
//此lock方法是ReentrantLock类里面的
public void lock() {
//如果是公平锁的话,sync.lock()方法调用的是FairSync类里面的lock方法。
//如果是非公平锁的话,sync.lock()方法调用的是NonfairSync类里面的lock方法。
sync.lock();
}
抽象类Sync的lock()抽象方法是由它的两个子类FairSync和NonfairSync来实现的。
而成员变量sync引用究竟是指向抽象类Sync的那个子类,则是根据ReentrantLock的构造函数决定的
1、 fair=true,以公平锁策略构造,sync=newFairSync();
2、 fair=false,以非公平锁策略构造,sync=newNonfairSync();
2.1 公平锁时抽象方法lock()的子类实现源码
//此lock方法是FairSync类里面的
final void lock() {
acquire(1);
}
lock()方法直接调用父类AQS(AbstractQueuedSynchronizer)里面的acquire方法。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
acquire方法的作用就是通过调用tryAcquire方法判断能否获取锁,不能的话就将当前线程加入同步队列中并阻塞。
AQS里面的acquire方法会先调用tryAcquire方法判断能否获取锁,其他的方法,如acquireQueued,addWaiter,selfInterrupt都是属于AQS的内部方法,可以参考JUC-AQS源码篇进行阅读。而tryAcquire方法是由AQS的子类来进行实现的。
在公平锁的情况下,tryAcquire方法是由FairSync类来进行实现的。其源码如下:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//获取state值
int c = getState();
if (c == 0) {
//锁没有被获取并且前面没有正在等待获取锁的线程
//那么就获取这把锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//锁已经被获取了,但是是当前线程获取的,那么当前线程就再获取一遍锁
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
此方法返回true的话,代表获取了锁,返回false的话就代表获取锁失败。
大致逻辑就是
1、 先获取state的值;
2、 如果state值等于0的话,代表这把锁没有被其他线程获取,注意因为这是一把公平锁,所以不能直接调用compareAndSetState(0,acquires)方法用CAS的方式来获取锁,我们得用hasQueuedPredecessors方法(AQS之hasQueuedPredecessors方法源码分析)来判断一下,是否已经有线程在等着获取这把锁,如果有,那我们就不能去跟它去抢这把锁(这就体现了公平,先来先到,既然你比我早,那么这把锁你先获取)直接返回false如果没有,那么我就调用compareAndSetState(0,acquires)来竞争这把锁,竞争到了,调用setExclusiveOwnerThread方法,将当前线程值赋值给exclusiveOwnerThread变量(这样做的原因是为了实现ReentrantLock的可重入性),然后返回true没有竞争到,就返回false;
3、 如果state值不等于0的,代表这把锁已经被其他线程获取了但是我们不能直接返回false因为ReentrantLock具有可重入性我们得通过current==getExclusiveOwnerThread()来判断已经获取这把锁的线程跟当前线程是不是同一个,如果是的话,那这把锁可以被此线程再次获取,只不过将state值加1,然后返回true如果不是就返回false;
注意:为什么当前线程已经获取锁了,当它再重复的获取这把锁时,state值为啥要加1?因为,当一个线程第一次获取这把锁时,state值为1,第二次重复获取这把锁时,如果state值不加1变成2的话,那么当它执行第一次释放锁的动作时,state值要-1,这样的话,state值就变成了0,但是,这个线程还是获取了这把锁(第二次获取的),这就不对了。
2.2 非公平锁时抽象方法lock()的子类实现源码
//此lock方法是NonfairSync类里面的
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
此方法是先调用compareAndSetState通过CAS的方法看能不能获取到锁,如果获取不到的话,就再调用父类AQS(AbstractQueuedSynchronizer)的acquire方法。从这就可以看出非公平性出来了,当前线程不管前面还有没有等着获取锁的线程,它先抢锁,抢到了就是它的了。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
acquire方法的作用就是通过调用tryAcquire方法判断能否获取锁,不能的话就将当前线程加入同步队列中并阻塞。
AQS里面的acquire方法会先调用tryAcquire方法判断能否获取锁,其他的方法,如acquireQueued,addWaiter,selfInterrupt都是属于AQS的内部方法,可以参考JUC-AQS源码篇进行阅读。而tryAcquire方法是由AQS的子类来进行实现的。
在非公平锁的情况下,tryAcquire方法是由NonfairSync类来进行实现的。其源码如下:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
NonfairSync的tryAcquire方法是直接调用了父类Sync的nonfairTryAcquire方法:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//获取state变量
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
此方法返回true的话,代表获取了锁,返回false的话就代表获取锁失败。
大致逻辑就是
1、 先获取state的值;
2、 如果state值等于0的话,代表这把锁没有被其他线程获取,注意因为这是一把非公平锁,我们直接就调用compareAndSetState(0,acquires)来竞争这把锁,竞争到了,调用setExclusiveOwnerThread方法,将当前线程值赋值给exclusiveOwnerThread变量(这样做的原因是为了实现ReentrantLock的可重入性),然后返回true没有竞争到,就返回false;
3、 如果state值不等于0的,代表这把锁已经被其他线程获取了但是我们不能直接返回false因为ReentrantLock具有可重入性我们得通过current==getExclusiveOwnerThread()来判断已经获取这把锁的线程跟当前线程是不是同一个,如果是的话,那这把锁可以被此线程再次获取,只不过将state值加1,然后返回true如果不是就返回false;
3.unlock()方法源码
我们知道ReentrantLock释放锁调用的入口方法是reentrantLock.unlock()方法。
public void unlock() {
sync.release(1);
}
此方法直接调用AQS的release方法:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
release方法通过调用tryRelease方法来尝试释放当前的锁,如果释放成功的话,就唤醒head节点的后继节点。tryRelease方法是由AQS的子类Sync类来实现的。
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
tryRelease方法的大致逻辑是:
1、 先获取state值然后将state值减1,赋值给c;
2、 然后判断当前线程跟获取锁的线程是不是同一个,因为只有获取锁的线程才能释放这把锁如果不是同一个,抛出异常如果是的话接着往下走;
3、 判断c的值是不是0,如果是的话,说明已经没有线程持有这把锁了将state值赋值为c,并调用setExclusiveOwnerThread方法将exclusiveOwnerThread变量赋值为null,返回true;
4、 如果c的值不是0,那说明当前线程还持有这把锁(当前线程重复获取了这把锁导致的),将state值赋值为c,返回false;