ReentrantLock 公平锁,非公平锁在加锁时候区别:
非公平锁:
1. 加锁时候,直接上来就cas尝试获取锁;
2. 获取失败,acquire时候,如果同步器状态空闲,不管队列中是否有排队的线程,直接cas操作抢占锁。
公平锁:
如果同步器状态空闲,先判断队列中是否含有排队的线程,没有则去抢占锁。
非公平锁尝试获取锁代码:
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
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;
}
公平锁尝试获取锁代码:
static final class FairSync extends Sync {
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;
}
}
ReentrantLock 加锁三大步骤:
- 当前线程尝试加锁tryAcquire方法,ReentrantLock 根据公平锁,非公平锁实现tryAcquire方法,区别参考上述描述。获取到锁返回,否则到第二步。
- addWaiter方法添加到队列尾部。
- acquireQueued方法一直自旋去获取锁:
a)判断当前节点是否处于队列第一个有效节点时候,如果是第一个有效节点,然后尝试获取锁,获取成功后,设置队列头结点后移;
b) 否则,shouldParkAfterFailedAcquire方法先判断是否应该park睡眠,参考shouldParkAfterFailedAcquire方法解析;
c) shouldParkAfterFailedAcquire方法校验是否应该park,则调用parkAndCheckInterrupt方法,此方法先LockSupport.park睡眠,被唤醒后,再进行中断检测。
ps: acquireQueued方法自旋中,出现异常,则cancelAcquire设置当前节点(线程)为取消竞争锁(该方法还需研究,比较复杂)
enq(Node node)方法:
自旋方式,队列为空,初始化队列,设置头结点指向空的节点,然后将该线程封装成的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;
}
}
}
}
shouldParkAfterFailedAcquire方法校验当前node(线程)是否应该park:
- 判断前一个节点的状态ws是Node.SIGNAL,则应该park,否则进行步骤2;
- 如果前一个节点的状态(ws)大于0,则一直往前遍历,直到找到节点状态不大于0为止;
- 如果前一个节点的状态(ws)<=0,则cas操作将其前一点状态变更为Node.SIGNAL,为了acquireQueued方法下次自旋适合能park
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
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;
}