ReentrantLock 源码分析
ReentrantLock
1)ReentrantLock 类实现了和 synchronized 一样的内存语义,同时该类提供了更加灵活多样的可重入互斥锁定操作。
2)ReentrantLock 实例可以被同一个线程多次获取,因此是可重入的互斥锁。
实例创建
/**
* 创建一个非公平的可重入互斥锁实例
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* fair=true:创建一个公平的可重入互斥锁实例,谁先加入同步队列谁先获取锁。
* fair=false:创建一个非公平的可重入互斥锁实例
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
读取锁【非公平锁】
/**
* 1)如果该可重入互斥锁没有被另一个线程保持,则获取该可重入互斥锁并立即返回,
* 将可重入互斥锁的保持计数设置为 1。
* 2)如果当前线程已经持有该可重入互斥锁,则将保持计数加 1,并且该方法立即返回。
* 3)如果该可重入互斥锁被另一个线程保持,当前线程将阻塞等待获取可重入互斥锁。
*/
public void lock() {
sync.acquire(1);
}
AbstractQueuedSynchronizer#acquire
/**
* 1)在独占模式下获取锁,忽略中断【线程被中断后,当尝试获取锁时会被清除中断状态并重新进入阻塞模式】
* 2)首先尝试进行一次锁获取,如果获取成功则直接返回;
* 如果获取失败,则将当前线程加入同步队列中阻塞等待获取锁。
*/
public final void acquire(int arg) {
/**
* 1)tryAcquire 首先尝试获取锁,获取成功则直接返回
* 2)获取失败后,通过 addWaiter 创建一个独占模式的节点,
* 并将当前线程驻留其上,通过 acquireQueued 将该节点加入同步队列阻塞等待获取锁。
* 3)如果尝试获取锁时线程被设置了中断标识,则当期线程中断自己。
*/
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
AbstractQueuedSynchronizer.selfInterrupt();
}
}
/**
* 非公平锁的同步对象
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
protected boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
/**
* ReentrantLock 锁的同步控制基础类
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 非公平地尝试获取锁操作
*/
@ReservedStackAccess
final boolean nonfairTryAcquire(int acquires) {
// 读取当前线程
final Thread current = Thread.currentThread();
// 读取同步状态值
final int c = getState();
// 1)如果同步状态为 0,表示没有线程持有锁
if (c == 0) {
// 尝试原子更新同步状态为 acquires,此处是 1
if (compareAndSetState(0, acquires)) {
// 如果设置成功,则表示当前线程获取锁成功,则设置同步器的独占线程为当前线程
setExclusiveOwnerThread(current);
// 成功获取锁返回 true
return true;
}
}
// 2)判断持有锁的线程是否是当前线程
else if (current == getExclusiveOwnerThread()) {
// 如果是当前线程,则表示锁重入,递增同步状态值
final int nextc = c + acquires;
// 同步状态值溢出
if (nextc < 0) {
throw new Error("Maximum lock count exceeded");
}
// 更新同步状态值
setState(nextc);
// 成功获取锁返回 true
return true;
}
// 锁已经被其他线程持有,获取失败则 false
return false;
}
}
AbstractQueuedSynchronizer#Node
/**
* 同步器队列节点
*/
static final class Node {
/** 标识当前节点在共享模式下等待 */
static final Node SHARED = new Node();
/** 标识当前节点在独占模式下等待 */
static final Node EXCLUSIVE = null;
/** 节点驻留线程由于超时或被中断而取消 */
static final int CANCELLED = 1;
/** 后继节点的驻留线程需要在当前节点被释放之后,被唤醒 */
static final int SIGNAL = -1;
/** 当前线程在条件队列中等待 */
static final int CONDITION = -2;
/**
* 下一次 acquireShared 操作需要被无条件往后传播,用于锁共享模式下
*/
static final int PROPAGATE = -3;
/**
* 同步器状态值
* SIGNAL: 当前节点的后继节点上驻留的线程被阻塞,当前节点被释放或取消时,
* 必须唤醒其后继节点上驻留的线程。
* CANCELLED: 当前节点由于超时或线程被中断而取消,节点永远不会离开该状态,
* 被取消的节点不会再次阻塞。
* CONDITION: 当前节点位于条件队列,当满足条件时,该节点的同步状态会被设置为 0,
* 并转移到同步队列中。
* PROPAGATE: releaseShared 操作需要被无条件向后传播,该值只能设置在头节点上。
* 0: 同步队列节点的初始状态。
*
* 非负值表示节点不需要信号【节点被取消】,在某些场景下可以简化使用。
* 同步队列节点的初始同步状态为 0,条件队列节点的初始同步状态为 -2.
*/
volatile int waitStatus;
/**
* 同步队列节点的前置节点,在入队列时设置,在出队列时设置为 null.
*/
volatile Node prev;
/**
* 同步队列节点的后置节点,在绕过取消的节点时更新,在节点出队列时置为 null。
* 被取消节点的 next 值被设置为节点本身。
*/
volatile Node next;
/**
* 驻留在节点的线程,在创建时设置,在使用后置为 null。
*/
volatile Thread thread;
/**
* 1)条件队列只在独占模式下被访问,nextWaiter 值为下一个在条件上等待的节点。
* 2)如果值为 SHARED,则表示该节点处于共享模式。
*/
Node nextWaiter;
/**
* 节点处于共享模式,则返回 true
*/
boolean isShared() {
return nextWaiter == Node.SHARED;
}
/**
* 读取当前节点的前置节点
*/
Node predecessor() {
final Node p = prev;
if (p == null) {
throw new NullPointerException();
} else {
return p;
}
}
/** Establishes initial head or SHARED marker. */
Node() {}
/** Constructor used by addWaiter. */
Node(Node nextWaiter) {
// 设置下一个等待节点
this.nextWaiter = nextWaiter;
// 将当期线程驻留其上
Node.THREAD.set(this, Thread.currentThread());
}
/** Constructor used by addConditionWaiter. */
Node(int waitStatus) {
Node.WAITSTATUS.set(this, waitStatus);
Node.THREAD.set(this, Thread.currentThread());
}
/** 尝试更新同步状态 */
boolean compareAndSetWaitStatus(int expect, int update) {
return Node.WAITSTATUS.compareAndSet(this, expect, update);
}
/** 尝试更新当前节点的后置节点 */
boolean compareAndSetNext(Node expect, Node update) {
return Node.NEXT.compareAndSet(this, expect, update);
}
// 设置当前节点的前置节点
void setPrevRelaxed(Node p) {
Node.PREV.set(this, p);
}
// VarHandle mechanics
private static final VarHandle NEXT;
private static final VarHandle PREV;
private static final VarHandle THREAD;
private static final VarHandle WAITSTATUS;
static {
try {
final MethodHandles.Lookup l = MethodHandles.lookup();
NEXT = l.findVarHandle(Node.class, "next", Node.class);
PREV = l.findVarHandle(Node.class, "prev", Node.class);
THREAD = l.findVarHandle(Node.class, "thread", Thread.class);
WAITSTATUS = l.findVarHandle(Node.class, "waitStatus", int.class);
} catch (final ReflectiveOperationException e) {
throw new Error(e);
}
}
}
AbstractQueuedSynchronizer#addWaiter
/**
* 以指定的模式 mode 创建节点,并将当前线程驻留其上,之后将该节点加入同步队列。
*/
private Node addWaiter(Node mode) {
// 创建节点并驻留当前线程
final Node node = new Node(mode);
for (;;) {
// 读取尾节点
final Node oldTail = tail;
// 1)尾节点不为 null,则直接将该节点加入到同步队列尾部
if (oldTail != null) {
// 设置前置节点
node.setPrevRelaxed(oldTail);
// 比较设置尾部节点
if (compareAndSetTail(oldTail, node)) {
// 设置成功,则更新旧尾节点的 next 为当前节点【新尾节点】
oldTail.next = node;
// 返回当前节点
return node;
}
// 2)初始化同步队列
} else {
initializeSyncQueue();
}
}
}
AbstractQueuedSynchronizer#initializeSyncQueue
/**
* 在第一次发生锁竞争时初始化同步队列的头、尾节点,
* 同步队列的头节点是一个傀儡节点,不驻留线程,新建时同步状态为 0,
* 阻塞节点加入队列后,同步状态被设置为 -1.
*/
private final void initializeSyncQueue() {
Node h;
// 原子设置头结点为新建节点
if (AbstractQueuedSynchronizer.HEAD.compareAndSet(this, null, h = new Node())) {
// 设置为节点为新建节点
tail = h;
}
}
AbstractQueuedSynchronizer#acquireQueued
/**
* 在独占、线程不可中断的模式下获取锁,当前线程已经在同步队列中。
*/
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
for (;;) {
// 读取当前节点的前置节点
final Node p = node.predecessor();
// 如果前置节点是 head,则尝试获取锁
if (p == head && tryAcquire(arg)) {
// 获取成功,则设置当前节点为 head
setHead(node);
p.next = null; // help GC
// 返回线程中断标识
return interrupted;
}
// 当前线程是否需要被阻塞
if (AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire(p, node)) {
// 阻塞当前线程,并等待唤醒,唤醒后返回其中断状态
interrupted |= parkAndCheckInterrupt();
}
}
// 线程运行过程中出现异常
} catch (final Throwable t) {
// 取消当前节点
cancelAcquire(node);
// 如果线程被设置中断标识
if (interrupted) {
// 则线程自我中断
AbstractQueuedSynchronizer.selfInterrupt();
}
throw t;
}
}
AbstractQueuedSynchronizer#shouldParkAfterFailedAcquire
/**
* 判断并更新前置节点的状态,如果线程需要被阻塞,则返回 true
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 读取前置节点的同步状态
final int ws = pred.waitStatus;
// 1)前置节点已经是 Node.SIGNAL,最后入队的节点被取消而踢除时出现。
if (ws == Node.SIGNAL) {
// 当前线程需要被阻塞,则返回 true
return true;
}
// 2)前置节点已经被取消
if (ws > 0) {
// 将被取消的前置节点踢除,直到遇到一个需要信号的节点为止
do {
// 踢除前置节点
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
// 将当前节点链接到新的前置节点后
pred.next = node;
} else {
/**
* waitStatus must be 0 or PROPAGATE.
* 将前置节点设置为 Node.SIGNAL,表示当前置节点被释放时,需要唤醒当前节点
* 再次获取锁失败时,当前节点将阻塞等待。
*/
pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
}
// 线程不需要被阻塞,再次尝试获取锁
return false;
}
AbstractQueuedSynchronizer#parkAndCheckInterrupt
/**
* 阻塞当前线程,并在线程被释放时【正常释放或被其他线程中断】
* 返回中断状态。
*/
private final boolean parkAndCheckInterrupt() {
// 阻塞当前线程
LockSupport.park(this);
// 返回并清除线程中断状态
return Thread.interrupted();
}
LockSupport#park
/**
* 在许可可用之前阻塞当前线程。
* 如果许可可用,则使用该许可,并且该调用立即返回;否则,当前线程将被阻塞。
* 以下场景下该方法调用才会返回:
* 1)其他某个线程调用将当前线程作为目标调用 unpark。
* 2)其他某个线程中断当前线程。
* 3)该调用不合逻辑地(即毫无理由地)返回。
*/
public static void park(Object blocker) {
// 读取当前线程
Thread t = Thread.currentThread();
// 设置 Thread 的 parkBlocker 属性为阻塞者 blocker
setBlocker(t, blocker);
// 阻塞当前线程
U.park(false, 0L);
// 线程被重新调度,则置空当前线程的 parkBlocker
setBlocker(t, null);
}
Unsafe#park
/**
* 阻塞当前线程
* 1)isAbsolute=false,time=0:直到其他线程调用 LockSupport#unpark 方法唤醒
* 目标线程,或目标线程被其他线程中断。
* 2)isAbsolute=false,time>0:除了线程被主动唤醒、线程被中断之外,指定的纳秒时间已经过去
* ,则线程被自动唤醒。
* 3)isAbsolute=true,time>0:除了线程被主动唤醒、线程被中断之外,
* 当前时间已经超过指定 Epoch 的时间戳,则线程被自动唤醒。
* 4)异常情况下被唤醒
*/
@HotSpotIntrinsicCandidate
public native void park(boolean isAbsolute, long time);
AbstractQueuedSynchronizer#cancelAcquire
/**
* 1)被取消节点是尾节点,则直接踢除
* 2)被取消节点是第一个需要获取锁的节点,则唤醒其后置节点
* 3)被取消节点在中间,则将前置节点和后置节点相连
* 取消正在进行的获取锁操作
*/
private void cancelAcquire(Node node) {
// 节点已经不存在,则直接返回
if (node == null) {
return;
}
// 清除节点驻留线程
node.thread = null;
// 跳过被取消的前置节点
Node pred = node.prev;
while (pred.waitStatus > 0) {
node.prev = pred = pred.prev;
}
// predNext is the apparent node to unsplice. CASes below will
// fail if not, in which case, we lost race vs another cancel
// or signal, so no further action is necessary.
final Node predNext = pred.next;
// 设置节点的同步状态为已经取消
node.waitStatus = Node.CANCELLED;
// 1)当前节点是尾节点,则比较更新尾部接地啊
if (node == tail && compareAndSetTail(node, pred)) {
// 尾部节点更新成功后,比较更新尾节点的 next 属性
pred.compareAndSetNext(predNext, null);
// 2)当前节点不是尾节点
} else {
/**
* 1)前置节点不是头节点,并且前置节点的同步状态为 Node.SIGNAL【
* 如果同步状态<=0,则将其设置为 Node.SIGNAL】,
* 并且前置节点的驻留线程不为 null【不是傀儡节点】。
*/
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
ws <= 0 && pred.compareAndSetWaitStatus(ws, Node.SIGNAL)) &&
pred.thread != null) {
// 读取后置节点
final Node next = node.next;
// 后置节点不为 null,并且其需要信号
if (next != null && next.waitStatus <= 0) {
// 则比较更新前置节点的 next 属性
pred.compareAndSetNext(predNext, next);
}
/**
* 2)前置节点是头节点,说明当前节点是需要获取锁的第一个节点,
* 当前节点被取消,需要唤醒其后继节点。
*/
} else {
unparkSuccessor(node);
}
// 被取消节点的 next 设置为节点本身
node.next = node; // help GC
}
}
AbstractQueuedSynchronizer#unparkSuccessor
/**
* 如果后继节点存在,则唤醒驻留其上的线程
*/
private void unparkSuccessor(Node node) {
// 读取节点同步状态
final int ws = node.waitStatus;
// 节点需要信号,则比较更新为 0
if (ws < 0) {
node.compareAndSetWaitStatus(ws, 0);
}
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
/**
* 读取后置节点
*/
Node s = node.next;
// 后置节点为 null 或后置节点已经被取消
if (s == null || s.waitStatus > 0) {
s = null;
/**
* 从尾部开始遍历,如果迭代节点不是当前节点,并且其不为 null,则进入循环体。
* 一旦遇到当前节点或前置节点为 null【遍历到头部】,则退出循环。
* 遍历完成之后,可以获取到当前节点后,第一个需要被唤醒的节点【如果存在】
*/
for (Node p = tail; p != node && p != null; p = p.prev) {
// 迭代节点需要信号
if (p.waitStatus <= 0) {
// 则更新需要被唤醒的节点
s = p;
}
}
}
// 目标节点存在
if (s != null) {
// 则唤醒驻留其上的线程
LockSupport.unpark(s.thread);
}
}
LockSupport#unpark
/**
* 给当前线程一个许可,如果当前线程被阻塞,则将其唤醒;
* 如果线程已经被唤醒,则下一次调用 park 操作形参 Thread 将不会被阻塞。
*/
public static void unpark(Thread thread) {
if (thread != null) {
// 唤醒目标线程
LockSupport.U.unpark(thread);
}
}
获取锁【公平锁】
/**
* 1)如果该可重入互斥锁没有被另一个线程保持,则获取该可重入互斥锁并立即返回,
* 将可重入互斥锁的保持计数设置为 1。
* 2)如果当前线程已经持有该可重入互斥锁,则将保持计数加 1,并且该方法立即返回。
* 3)如果该可重入互斥锁被另一个线程保持,当前线程将阻塞等待获取可重入互斥锁。
*/
@Override
public void lock() {
sync.acquire(1);
}
AbstractQueuedSynchronizer#acquire
/**
* 1)在独占模式下获取锁,忽略中断【线程被中断后,当尝试获取锁时会被清除中断状态并重新进入阻塞模式】
* 2)首先尝试进行一次锁获取,如果获取成功则直接返回;
* 如果获取失败,则将当前线程加入同步队列中阻塞等待获取锁。
*/
public final void acquire(int arg) {
/**
* 1)tryAcquire 首先尝试获取锁,获取成功则直接返回
* 2)获取失败后,通过 addWaiter 创建一个独占模式的节点,
* 并将当前线程驻留其上,通过 acquireQueued 将该节点加入同步队列阻塞等待获取锁。
* 3)如果尝试获取锁时线程被设置了中断标识,则当期线程中断自己。
*/
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
AbstractQueuedSynchronizer.selfInterrupt();
}
}
/**
* 公平锁的同步对象
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
/**
* 公平锁的获取操作
*/
@Override
@ReservedStackAccess
protected boolean tryAcquire(int acquires) {
// 读取当前线程
final Thread current = Thread.currentThread();
// 读取同步状态
final int c = getState();
// 1)同步状态为 0
if (c == 0) {
/**
* 1)同步队列中没有等待获取锁的线程或等待线程就是当前线程,
* 则比较更新同步状态。
*/
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 更新成功则表示获取锁成功,返回 true
setExclusiveOwnerThread(current);
return true;
}
}
// 2)当前线程是持有锁的线程,锁重入
else if (current == getExclusiveOwnerThread()) {
// 递增同步状态值
final int nextc = c + acquires;
if (nextc < 0) {
throw new Error("Maximum lock count exceeded");
}
// 更新同步状态值,获取成功返回 true
setState(nextc);
return true;
}
return false;
}
}
AbstractQueuedSynchronizer#hasQueuedPredecessors
/**
* 是否有线程在等待获取锁
*/
public final boolean hasQueuedPredecessors() {
// 读取尾节点
final Node t = tail; // Read fields in reverse initialization order
// 读取头节点
final Node h = head;
Node s;
/**
* 同步队列中存在等待节点,并且
* 1)head.next==null,同步队列初始化时,说明已经有节点正在加入到同步队列中
* 2)第一个等待获取锁的节点驻留线程不是当前线程【如果是当前线程,则可以重入】
*/
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
释放锁【公平锁和非公平锁一致】
/**
* 尝试释放锁
*/
@Override
public void unlock() {
sync.release(1);
}
AbstractQueuedSynchronizer#release
/**
* 在独占模式下释放锁
*/
public final boolean release(int arg) {
// 尝试释放锁
if (tryRelease(arg)) {
// 读取头节点
final Node h = head;
// 头结点的同步状态不为 0,则唤醒其后置节点
if (h != null && h.waitStatus != 0) {
// 唤醒目标节点的后继节点
unparkSuccessor(h);
}
return true;
}
return false;
}
Sync#tryRelease
@Override
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
// 计算新的同步状态值
final int c = getState() - releases;
// 当前线程不是持有锁的线程
if (Thread.currentThread() != getExclusiveOwnerThread()) {
// 抛出 IllegalMonitorStateException 异常
throw new IllegalMonitorStateException();
}
boolean free = false;
// 同步状态值为 0,标识锁已经没有被线程持有
if (c == 0) {
free = true;
// 清空锁持有线程
setExclusiveOwnerThread(null);
}
// 更新状态值
setState(c);
return free;
}
可中断地获取锁
/**
* 可响应中断地获取锁,一旦目标线程被其他中断,则抛出 InterruptedException 异常。
*/
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
AbstractQueuedSynchronizer#acquireInterruptibly
/**
* 1)在独占模式下获取锁
* 2)首先尝试进行一次锁获取,如果获取成功则直接返回;
* 如果获取失败,则将当前线程加入同步队列中阻塞等待获取锁。
* 3)如果目标线程被其他线程中断,则抛出 InterruptedException 异常
*/
public final void acquireInterruptibly(int arg)
throws InterruptedException {
// 返回并清除当前线程的中断标识,如果线程已经被中断
if (Thread.interrupted()) {
// 直接抛出 InterruptedException 异常
throw new InterruptedException();
}
// 尝试获取锁
if (!tryAcquire(arg)) {
// 获取失败,则可中断地再次获取锁
doAcquireInterruptibly(arg);
}
}
AbstractQueuedSynchronizer#doAcquireInterruptibly
/**
* 在独占模式下获取锁,可响应线程中断
*/
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
try {
for (;;) {
// 读取当前节点的前置节点
final Node p = node.predecessor();
// 如果前置节点是 head,则尝试获取锁
if (p == head && tryAcquire(arg)) {
// 获取成功,则设置当前节点为 head
setHead(node);
p.next = null; // help GC
return;
}
// 当前线程是否需要被阻塞,如果需要,则阻塞当前线程,并等待唤醒,唤醒后返回其中断状态
if (AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) {
// 如果当前线程被其他线程中断
throw new InterruptedException();
}
}
} catch (final Throwable t) {
// 取消正在获取的锁操作
cancelAcquire(node);
throw t;
}
}
尝试获取锁
/**
* 尝试进行一次获取锁操作
*/
@Override
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
带超时时间的锁获取
/**
* 在指定的时间内,尝试获取锁,一旦超时,则直接返回结果。
* 可响应线程中断。
*/
@Override
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
// 将目标时间转换为纳秒
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
AbstractQueuedSynchronizer#tryAcquireNanos
/**
* 在独占模式下尝试获取锁,
* 1)线程被中断,则直接抛出 InterruptedException 异常
* 2)超出目标时间,则直接返回
*/
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
// 获取并清除当前线程的中断标识
if (Thread.interrupted()) {
throw new InterruptedException();
}
/**
* 1)尝试进行锁获取,成功则直接返回 true。
* 2)获取失败,则基于指定的超时时间再次尝试获取锁,直到超时为止。
*/
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
AbstractQueuedSynchronizer#doAcquireNanos
/**
* 在独占、超时模式下获取锁
*/
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
// 超时时间为负数,则直接返回获取失败
if (nanosTimeout <= 0L) {
return false;
}
// 计算结束时间
final long deadline = System.nanoTime() + nanosTimeout;
// 将当前线程加入到同步队列
final Node node = addWaiter(Node.EXCLUSIVE);
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
return true;
}
// 计算剩余时间
nanosTimeout = deadline - System.nanoTime();
// 已经超时
if (nanosTimeout <= 0L) {
// 取消当前线程的锁获取操作
cancelAcquire(node);
return false;
}
/**
* 当前线程是否需要被阻塞,
* 1)如果超时时间大于 1000 纳秒,则阻塞当前线程。
* 2)否则进行自旋获取锁操作。
*/
if (AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > AbstractQueuedSynchronizer.SPIN_FOR_TIMEOUT_THRESHOLD) {
// 最多阻塞当前线程 nanosTimeout 纳秒
LockSupport.parkNanos(this, nanosTimeout);
}
// 当前线程是否被中断
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
} catch (final Throwable t) {
cancelAcquire(node);
throw t;
}
}
/**
* 在许可可用之前阻塞当前线程。
* 如果许可可用,则使用该许可,并且该调用立即返回;否则,当前线程将被阻塞。
* 以下场景下该方法调用才会返回:
* 1)其他某个线程将当前线程作为目标调用 unpark。
* 2)其他某个线程中断当前线程。
* 3)线程阻塞已经超出 nanos 纳秒
* 4)该调用不合逻辑地(即毫无理由地)返回。
*/
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
final Thread t = Thread.currentThread();
LockSupport.setBlocker(t, blocker);
LockSupport.U.park(false, nanos);
LockSupport.setBlocker(t, null);
}
}
状态查询操作,基于队列的状态查询是不精确的
/**
* 当前可重入互斥锁是不是公平锁
*/
public final boolean isFair() {
return sync instanceof FairSync;
}
/**
* 锁是不是被当前线程持有
*/
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
/**
* 锁是不是已经被占用,同步状态不为 0
*/
public boolean isLocked() {
return sync.isLocked();
}
/**
* 当前线程持有同步状态的计数值
*/
public int getHoldCount() {
return sync.getHoldCount();
}
ReentrantLock 源码分析的更多相关文章
- Java并发编程-ReentrantLock源码分析
一.前言 在分析了 AbstractQueuedSynchronier 源码后,接着分析ReentrantLock源码,其实在 AbstractQueuedSynchronizer 的分析中,已经提到 ...
- java多线程---ReentrantLock源码分析
ReentrantLock源码分析 基础知识复习 synchronized和lock的区别 synchronized是非公平锁,无法保证线程按照申请锁的顺序获得锁,而Lock锁提供了可选参数,可以配置 ...
- ReentrantLock源码分析--jdk1.8
JDK1.8 ArrayList源码分析--jdk1.8LinkedList源码分析--jdk1.8HashMap源码分析--jdk1.8AQS源码分析--jdk1.8ReentrantLock源码分 ...
- JUC AQS ReentrantLock源码分析
警告⚠️:本文耗时很长,先做好心理准备,建议PC端浏览器浏览效果更佳. Java的内置锁一直都是备受争议的,在JDK1.6之前,synchronized这个重量级锁其性能一直都是较为低下,虽然在1.6 ...
- ReentrantLock 源码分析以及 AQS (一)
前言 JDK1.5 之后发布了JUC(java.util.concurrent),用于解决多线程并发问题.AQS 是一个特别重要的同步框架,很多同步类都借助于 AQS 实现了对线程同步状态的管理. A ...
- JUC之ReentrantLock源码分析
ReentrantLock:实现了Lock接口,是一个可重入锁,并且支持线程公平竞争和非公平竞争两种模式,默认情况下是非公平模式.ReentrantLock算是synchronized的补充和替代方案 ...
- Java并发编程之ReentrantLock源码分析
ReentrantLock介绍 从JDK1.5之前,我们都是使用synchronized关键字来对代码块加锁,在JDK1.5引入了ReentrantLock锁.synchronized关键字性能比Re ...
- Java并发系列[5]----ReentrantLock源码分析
在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可 ...
- Java并发编程 ReentrantLock 源码分析
ReentrantLock 一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大. 这个类主要基于AQS(Abst ...
- concurrent(三)互斥锁ReentrantLock & 源码分析
参考文档:Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock:http://www.cnblogs.com/skywang12345/p/3496101.html Reentr ...
随机推荐
- [LeetCode] 211. 添加与搜索单词 - 数据结构设计
题目链接:https://leetcode-cn.com/problems/add-and-search-word-data-structure-design/ 题目描述: 设计一个支持以下两种操作的 ...
- git将某个分支的代码完全覆盖另一个分支
假设每个人有个开发分支,想隔一段时间就把自己的开发分支上的代码保持和测试分支一直,则需要如下操作: 1.我想将test分支上的代码完全覆盖dev分支,首先切换到dev分支git checkout de ...
- Jmeter学习总结
学习内容: 1.用户定义的变量 作用:多个地方使用同一个值,且该值在不同的环境下不同,方便脚本在不同环境下运行时修改. 2.基本的HTTP请求,请求方式:get 3.传入参数为json 4.HTTP信 ...
- windows使用cmd命令输出文件清单和文件树
输出目录树:tree /f > d:\filetree.txt 输出目录清单:dir /s /b > d:\filelist.txt
- slots_doc_call属性
class Foo: "这时文档doc属性" __slots__ = ["name","age"] #[“name”=None,“age&q ...
- Linux 安装FTP服务
Linux 安装FTP服务,简单入门 环境: 虚拟机:Oracle VM VirtualBox. 系统:CentOS 7. (1)判断是否安装了ftp: rpm -qa | grep vsftpd 或 ...
- noip考前抱佛脚 数论小总结
exCRT 求解韩信点兵问题,常见的就是合并不同\(mod\). 先mo一发高神的板子 for(R i=2;i<=n;++i){ ll Y1,Yi,lcm=Lcm(p[i],p[1]); exg ...
- 非父子组件通过事件传值-vue
1.创建中央事件总线:额外的 new Vue()2.$emit 触发事件3.$on 监听事件 在使用组件的文件中: <template> <div> <x-test :b ...
- Django框架架构总览
Django框架架构总览 理解Django是如何运作的 条目创建于 2013-08-14 1464 views 服务器君一共花费 15.204 ms 进行了 4 次数据库查询,努力地为 ...
- VUE+Ionic,项目搭建&打包成APK
安装Vue&创建Vue工程 1.安装Vue CLI: npm install -g vue-cli 2.创建新的Vue项目,创建最后一步会提醒是否使用npm install 自动安装,如果选择 ...