该锁是 独占锁 但是分为 公平 非公平
公平 指的是 先进先出的概念 而 非公平 只要state=0 不管 有没有排队 都能有机会cas拿到锁
AQS 主要负责了 维护 Node双向链表 等待的节点 的操作,获取 更改 state等
ReentrantLock 源码相对比较简单 读者 可以 读过之后再去看 readWriteLock 会亲轻松很多
public void lock() {
sync.lock(); 当点机时 会出现公平非公平 上述已有说 两者差别不是很大
}
public final void acquire(int arg) {
if (!tryAcquire(arg) && 尝试获取锁
获取不成功 那么加入到等待链表种 然后 自旋 2次没拿到锁 就park 等待 拿到锁的线程唤醒
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
如果 等于0 代表 没有线程 拿到这把锁 【独占】
if (c == 0) {
看下面方法详解 公平 非公平 只是少了这个方法而已
if (!hasQueuedPredecessors() &&
如果等待链表不存在 或者当前线程就是 headNode.nextNode 应该唤醒的线程那么
直接cas 尝试拿锁
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
如果上面不满足 看看是不是 重入 如果是 重入的状态 那么也返回 ture
重入一次加1 拿到锁也是加1
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
这个方法主要是为了 拦截的 拦截 虽然state=0了 但是 他不是 headNode.nextNode.thread 那么同样加入到等待节点链表中
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());
}
如果 返回fasle 那么需要 加入到等待链表中 使得线程watting
private Node addWaiter(Node mode) {
封装一下 reentrantLock默认是独占的 所以这个node 也是 一个标记的意思
Node node = new Node(Thread.currentThread(), mode);
如果tail节点 不等于空代表不是第一次进来了 ,那么直接 把自己设置成 尾部节点
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
如果为空 那么自旋
enq(node);
return node;
}
private Node enq(final Node node) {
首先初始化一个 标志性的头 代表拿到锁的线程 然后把自己设置成 尾部节点
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
加入到 双向链表中之后 这儿 开始真正的 自旋获取锁 但是默认只有2次机会
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
中断标记 不是很重要 在可中断的lock中有用
boolean interrupted = false;
for (;;) {
获取preNode 因为在 设计之初就定义了 即使被唤醒 也要判断 当前节点的上一个节点
是不是 头节点 按序唤醒 【这与公平 非 公平 无关】
headNode代表了持有锁的线程
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
拿到锁 那么把自己设置成 头节点 代表自己已经拿到锁了
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
见下面方法解析
if (shouldParkAfterFailedAcquire(p, node) &&
第二次还没拿到锁 直接 进入等待状态 等待唤醒 唤醒之后程序计数器 继续从此执行
然后自旋
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
如果等于 -1 代表后续有线程需要 当前拿到锁的线程唤醒 如果> 0 代表canncel
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
获取peNode的状态 如果是signal代表后续线程 也就是当前线程需要被前驱节点线程唤醒
然后 跳过 状态 等于1的 >0 代表该等待节点取消了等待 ,移除 然后cas设hi前驱节点的waitState=signal 然后再次 循环 的时候 就安心 park 了
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
=============释放锁===========================
public final boolean release(int arg) {
尝试释放
if (tryRelease(arg)) {
Node h = head;
释放成功看看有没有需要 唤醒的节点 如果有 执行unpark
if (h != null && h.waitStatus != 0)
这个地方其实会有个问题 就是
一个等待的线程 在加入到节点链表中之后 但是没拿到cpu的执行器 waitStatus并没有改掉 然后切到 拿到锁的线程一直执行,就会无法unpark 但是就是这么设计的 也没啥好说的
unparkSuccessor(h);
return true;
}
return false;
}
这个方法 首先 改变 当前节点 wait状态=0初始话 因为 当前节点已经 可以丢弃了
然后找到下一个节点 唤醒 等于null方向遍历 因为enq 中cas后 没有把nextNode存到 上一个节点中 所以需要方向
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}