ReentrantLock
锁的获取分别实现了不可中断获取锁lock
,尝试获取锁tryLock
(获取成功返回true,获取失败返回false),可中断获取锁lockInterruptibly
;锁的释放unlock
很简单,直接调用的AQS的模板方法release
,不需要区分公平性。
1、ReentrantLock#lock
ReentrantLock#lock
调用了sync.lock()
,Sync
中lock()
是一个抽象函数,具体的实现在其子类NonfairSync
和FairSync
中。
public void lock() {
sync.lock();
}
abstract static class Sync extends AbstractQueuedSynchronizer {
/**
-
Performs {@link Lock#lock}. The main reason for subclassing
-
is to allow fast path for nonfair version.
*/
abstract void lock();
}
(1)ReentrantLock.NonfairSync
ReentrantLock.NonfairSync#lock
实现了Sync中的抽象方法lock,其非公平性体现在,一上来就抢锁设置statecompareAndSetState(0, 1)
,如果抢锁成功就设置当前线程为持有独占锁的线程setExclusiveOwnerThread(Thread.currentThread())
。
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
-
Performs lock. Try immediate barge, backing up to normal
-
acquire on failure.
*/
final void lock() {
//非公平,直接抢锁
if (compareAndSetState(0, 1))
//抢成功,设置当前独占锁的线程为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
//没有抢到
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
如果一上来抢锁失败则进入AQS的模板方法acquire(1)
,acquire(1)
中还会再进行一次抢锁tryAcquire
,抢锁失败才进入AQS队列操作acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
。(acquire
详解请看拙作《AQS源码解读——从acquireQueued探索独占锁实现原理,如何阻塞?如何唤醒?》)
//AbstractQueuedSynchronizer#acquire
public final void acquire(int arg) {
//若没有抢到锁,则进入等待队列
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//自己中断自己
selfInterrupt();
}
加锁的通用模板acquire()
已经在AQS中实现,子类只需要实现tryAcquire
即可。NonfairSync#tryAcquire
调用了父类的Sync#nonfairTryAcquire
。具体逻辑如下:
-
查看当前state,是否有线程持有锁,没有
state=0
则继续抢锁。 -
有线程持有锁,判断持有锁的线程是否是当前线程,是就重入。
//ReentrantLock.Sync#nonfairTryAcquire
final boolean nonfairTryAcquire(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;
if (nextc < 0) // overflow
throw new Error(“Maximum lock count exceeded”);
//重入
setState(nextc);
return true;
}
return false;
}
(2)ReentrantLock.FairSync
FairSync
和NonfairSync
的主要区别在于,FairSync
不会一上来就抢锁,而是先判断队列中是否有其他线程在等待锁,没有再抢锁。
代码的模板逻辑和NonfairSync
类似,区别在于FairSync
中tryAcquire
的实现。
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
-
Fair version of tryAcquire. Don’t grant access unless
-
recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
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;
}
}
FairSync
的公平性主要体现在tryAcquire
判断当前没有线程持有锁时,不会立即去抢锁,而是判断AQS队列中是否有其他线程也在等待锁,没有才去抢锁。如果有线程正持有锁,判断是否是当前线程,是则重入。
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
2、ReentrantLock#lockInterruptibly
lock
是不可中断锁,lockInterruptibly
是可中断锁,其直接调用了AQS的模板方法acquireInterruptibly
。
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
acquireInterruptibly
和doAcquireInterruptibly
代码中分别响应中断抛出异常InterruptedException
。
(acquireInterruptibly
和doAcquireInterruptibly
详解请看拙作《AQS源码解读——从acquireQueued探索独占锁实现原理,如何阻塞?如何唤醒?》)
3、ReentrantLock#tryLock
tryLock
尝试获取锁,调用的是sync.nonfairTryAcquire(1)
,不会涉及到AQS队列操作,获取锁成功返回true,失败返回false。
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
tryLock
还有一个重载方法,可传入一个超时时长timeout
和一个时间单位TimeUnit
,超时时长会被转为纳秒级。
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
tryLock(long timeout, TimeUnit unit)
直接调用了AQS的模板方法tryAcquireNanos
,也具备了响应中断,超时获取锁的功能:
-
若一开始获取锁
tryAcquire
失败则进入AQS同步队列doAcquireNanos
。 -
进入同步队列后自旋1000纳秒,还没有获取锁且判断应该阻塞,则会阻塞一定时长。
-
超时时长到线程自动唤醒,再自旋还没获取锁,且判断超时则返回false。
(tryAcquireNanos
详解请看拙作《AQS源码解读——从acquireQueued探索独占锁实现原理,如何阻塞?如何唤醒?》)
4、ReentrantLock#unlock
ReentrantLock
释放锁的过程没有区分公平性,调用的是AQS的模板方法release()
,其基本逻辑如下:
-
释放锁
tryRelease
。 -
唤醒后继节点
unparkSuccessor
。(unparkSuccessor
详解请看拙作《AQS源码解读——从acquireQueued探索独占锁实现原理,如何阻塞?如何唤醒?》)
//ReentrantLock
public void unlock() {
sync.release(1);
}
//AbstractQueuedSynchronizer
public final boolean release(int arg) {
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
阿里一直到现在。**
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-f0LEzYV8-1710780393675)]
[外链图片转存中…(img-8957K9Xi-1710780393676)]
[外链图片转存中…(img-VGjM3CRw-1710780393676)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-OyDD6I7P-1710780393677)]