ReetrantLock非公平锁总结:
-
首先通过Unsafe类的CAS方法尝试将当前AQS锁的状态state从0设置为1,如果能够修改成功,那么在AbstractOwnableSynchronizer同步器中设置持有锁的线程为当前线程。
-
如果修改失败那么执行acquire方法
1)通过tryAcquire方法再次尝试获取锁资源
(1)获取AQS中的state次数,如果为0表示已经释放了锁资源,那么就可以通过自旋再次尝试获取锁资源,获取成功将同步器的持有锁的线程设置为当前线程。
(2)判断是否为重入操作,如果是重入操作那么将AQS的State次数+1,如果+1后的值变为了负数则表示超出了可重入的最大值,那么抛出异常。如果没有问题就结束运行。
(3)如果仍然没有获取锁资源,那么将当前线程封装成一个Node节点追加到AQS队列中。
(4)然后在acquireQueued方法中判断是否上一个节点是头结点,如果前驱结点是头结点就再次开启自旋获取锁,获取成功了就将当前节点设为头结点,将之前的头结点的next指针设置为null帮助GC回收。如果前驱结点不是头结点并且处于唤醒状态,那么就将当前节点的线程设置为阻塞。
(5)在设置阻塞的过程中极小的可能会发生异常,那么执行cancelAcquire方法,使该节点以及该节点之前的所有失效节点从AQS队列中清除出来。将一个有效的前驱节点指向一个有效的后继节点。
Node节点
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
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;
//标识下一个共享请求无条件传播
static final int PROPAGATE = -3;
//记录当前节点的状态
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
final boolean isShared() {
return nextWaiter == SHARED;
}
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else return p;
}
Node() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
非公平锁
1. 非公平锁调用了NonfairSync静态内部类进行加锁
/**
* 获取了锁资源的次数,当前线程每释放一次-1,每获取一次+1(可重入锁)
*/
private volatile int state;
//NonfairSync非公平锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
//通过CAS尝试将state从0修改为1,如果为true则修改成功
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
//尝试获取锁资源
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
// AbstractQueuedSynchronizer.class
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
2. 如果CAS失败则执行acquire(int arg)
public final void acquire(int arg) {
//tryAcquire(args)再次尝试获取锁资源,如果尝试成功,返回true
//当尝试后仍然失败
if (!tryAcquire(arg) &&
//获取锁资源失败后,将当前线程封装成一个Node,追加到AQS队列中
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//线程中断
selfInterrupt();
}
2.1 nonfairTryAcquire再次尝试获取锁资源
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取AQS的state的值
int c = getState();
//如果state==0 代表之前占有锁的线程已经将锁释放了,我可以再次尝试占有锁资源
if (c == 0) {
//通过CAS再次尝试获取锁资源
if (compareAndSetState(0, acquires)) {
//如果获取成功,设置ExclusiveOwnerThread为当前线程
setExclusiveOwnerThread(current);
return true; }
}
//如果当前线程是占有锁资源的线程 说明进行了重入锁的重入操作
else if (current == getExclusiveOwnerThread()) {
//将state+1
int nextc = c + acquires;
//如果+1后小于0,也就是+1后超出了可重入的最大值,那么抛出异常
if (nextc < 0) // overflow
//Maximum lock count exceeded 超过了最大锁数
// 01111 + 1 = 11111 变为了负数
throw new Error("Maximum lock count exceeded");
//没有问题就重新设置state值
setState(nextc);
return true; }
return false;
}
2.2 addWaiter
//说明前面获取资源失败,放到队列中等待
private Node addWaiter(Node mode) {
//创建Node节点,并且设置thread为当前线程,设置为排它锁
Node node = new Node(Thread.currentThread(), mode);
// 获取AQS队列中的尾部节点
Node pred = tail;
//如果为空则表示队列中什么都没有
//不为空则表示队列中存在其他节点 那么做一个添加节点的操作
if (pred != null) {
//将当前节点的上一个节点的指针指向队列尾部的节点
node.prev = pred;
//通过CAS的方式将tail节点设置为当前节点
if (compareAndSetTail(pred, node)) {
//将之前尾节点的next指向当前节点
pred.next = node;
return node;
}
}
//将该节点放到队列
enq(node);
//然后返回
return node;
}
2.2.1 enq
//如果进入这个方法表示现在AQS队列为空,或者之前CAS的操作失败
//那么重新往队列尾部添加
private Node enq(final Node node) {
for (;;) {
//重新获取队列尾节点
Node t = tail;
//如果为空则表示没人排队
if (t == null) {
//初始化一个Node作为head 而这个head没有意义。
if (compareAndSetHead(new Node()))
//将尾部也指向了这个没有意义的head
tail = head;
} else {
//有人排队则执行链表的添加操作
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
3. acquireQueued
//已经将node加入到了队列中了,才会执行这个方法
final boolean acquireQueued(final Node node, int arg) {
//设置锁资源的标识 true为没有拿到锁资源 false为拿到了锁资源
boolean failed = true;
try {
// 设置阻塞标识
boolean interrupted = false;
for (;;) {
//获取当前节点的前驱结点
final Node p = node.predecessor();
//如果上一个节点是头结点,说明队列中我是第一个节点
//那么我就再次尝试获取锁资源
if (p == head && tryAcquire(arg)) {
//如果获取到了那么将head节点设置为当前节点
setHead(node);
//将之前得到的前驱结点的next设置为null 提高GC效率
p.next = null; // help GC
//设置为false表示我拿到了锁资源
failed = false;
//拿到锁资源了 那么就返回false
return interrupted;
}
//只有当上一个节点状态为-1才会将线程阻塞,等待唤醒获取锁资源
//parkAndCheckInterrupt调用LockSupport.park()方法将线程阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//将阻塞标识设置为true
interrupted = true;
}
} finally {
//这里执行的几率约等于0
if (failed)
cancelAcquire(node);
}
}
3.1 shouldParkAfterFailedAcquire
// pred是前驱结点 node是当前节点
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//获取上一个节点的状态
int ws = pred.waitStatus;
//如果上一个节点状态为SIGNAL 也就是-1,表示上一个节点是没问题的
//那么表示当前节点需要被唤醒
if (ws == Node.SIGNAL)
return true;
//如果上一个节点状态大于0 表示上一个节点已经失效了
if (ws > 0) {
//那么将该节点指向上一个节点的指针指向上上个节点,直到指向的节点没有失效
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
//将找到的前驱节点的next指针指向当前节点
pred.next = node;
} else {
//小于等于0且不等于-1
//将上一个有效节点状态修改为-1 因为只有是-1才会唤醒后继节点
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
3.2 parkAndCheckInterrupt
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
3.3 cancelAcquire
private void cancelAcquire(Node node) {
// 健壮性判断
if (node == null)
return;
//将当前node的线程设置为null,竞争锁资源和我没关系了
node.thread = null;
//获取当前节点的前驱结点
Node pred = node.prev;
//如果前驱节点的状态大于0,说明这是一个失效节点
while (pred.waitStatus > 0)
//找到前驱中最近的非失效节点
node.prev = pred = pred.prev;
// 尾节点执行的操作
// 如果当前节点是最后一个节点,将尾节点设置为最近的有效节点
if (node == tail && compareAndSetTail(node, pred)) {
// 通过CAS方式,将尾节点的next设为null
compareAndSetNext(pred, predNext, null);
} else {
int ws;
// 中间节点执行的操作
// 如果上一个节点不是头结点
if (pred != head &&
//获取上一节点状态是不是有效
// 判断上一节点的状态是否为-1
((ws = pred.waitStatus) == Node.SIGNAL ||
// 如果不是-1则将上一节点的状态通过CAS设置为-1
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
//并且上一节点的线程不为空
pred.thread != null) {
// 获取当前节点的后继节点
Node next = node.next;
// 如果后继节点不为空并且后继节点是一个有效节点
if (next != null && next.waitStatus <= 0)
//通过CAS将前一个节点的next指针指向当前节点的后继节点
compareAndSetNext(pred, predNext, next);
} else {
// 头结点执行的操作
//如果上一节点是头结点,唤醒当前节点的后继节点
unparkSuccessor(node);
}
//将当前节点的后继节点指向当前节点 帮助GC回收
node.next = node;
}
}