void acquire(int arg):独占式获取同步状态,如果获取失败则将当前现场插入同步队列进行等待。
1)调用tryAcquire使用CAS尝试再次获取同步状态,若成功方法直接返回,把当前线程置为持有锁线程;
若再次尝试失败,调用addWaiter()
2)剖析源码:将当前线程封装为Node节点后尾插入同步队列
private Node addWaiter(Node mode) {
//将当前线程以指定的模式封装为节点
Node node = new Node(Thread.currentThread(), mode);
// 拿到当前同队列的尾节点
Node pred = tail;
if (pred != null) {
node.prev = pred;
//CAS将当前节点尾插入同步队列
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//当前队列为空或者CAS失败时会调用enq方法
enq(node);
return node;
}
//当同步队列为空时,完成队列初始化操作以及不断进行CAS操作将当前节点尾插入同步队列
private Node enq(final Node node) {
//死循环-不断自旋
for (;;) {
//拿到尾节点
Node t = tail;
//当前队列为空
if (t == null) {
//完成队列初始化操作,头结点中不放数据,只是作为起始标记,lazy-load,在第一次用的时候new
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
//不断将当前节点使用CAS尾插入队列中直到成功为止
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
//final修饰,子类只能用,不能修改
final boolean acquireQueued(final Node node, int arg) {
//设置失败状态,初始化为true
boolean failed = true;
try {
//设置中断状态,默认为false,
boolean interrupted = false;
//不断自旋
for (;;) {
//拿到当前节点前驱节点
final Node p = node.predecessor();
//当前节点前驱节点为头结点并且再次获取同步状态成功
if (p == head && tryAcquire(arg)) {
//将当前节点置为头结点
setHead(node);
//将前驱节点出队
p.next = null;
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
//将当前节点设置为取消状态;取消状态设置为1
cancelAcquire(node);
}
}
节点从同步队列获取同步状态的前提:
只有当前驱节点为头结点时,线程才有机会获取同步状态
前驱节点不是头结点或者获取同步状态失败时:
尝试将前驱节点状态改为 Node.SIGNAL,表示此时当前节点应该被阻塞
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//获取前驱节点状态
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
//表示应该将当前节点阻塞
return true;
//前驱节点被取消了
if (ws > 0) {
//一直向前找节点状态不是取消状态的前驱节点
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//将前驱节点状态置为-1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
获取锁失败等待竞争锁的队列是同步队列
等待被唤醒的队列是等待队列,等待队列没有被唤醒就不存在竞争。