Article directory
After reading for a few days, I couldn't come up with anything. I took the jdk source code and found out the price and comments, and memorized the eight-part essay.
tryAcquire
线程5 设置state=1,直接执行,其他线程acquire(1)
其他线程获取state不等0,返回false,执行addWaiter,获取到同线程5
- 公平锁 判断state==0并且 前面没有人排队,才会cas
- 如果前面有排队,失败执行后面的入队操作
头尾节点都为空或者头尾节点同一个表示不同排队(可能此时有其他线程,但是不要紧,后面还是尝试获取)
第二个节点为空 不用排队
第二个节点的线程不是当前线程排队
- 非公平锁直接判断state==0,cas抢占
addWaiter
- 创建node节点
- 判断当前tail是不是空,不是空
- 把node节点的头设置为tail 会不会多个节点的前节点都是tail?似乎关系不大
- 需要把tail节点的尾设置为node cas
- tail是空,执行enq(node)
enq(node)
死循环
判断tail是不是空,可能判断的是够其他线程已经初始化了
- 空 初始化并设置头尾节点
- 不为空 把node节点的头设置为tail 会不会多个节点的前节点都是tail?似乎关系不大
- 需要把tail节点的尾设置为node cas
addWaiter肯定成功,返回刚刚入队的node
acquireQueued(node, arg)
循环
- 判断当前节点的前置节点是头结点,tryAcquire(1)
- 获取成功设置头结点为当前节点
- 不是头结点
- 判断需不需要挂起shouldParkAfterFailedAcquire(Node pred, Node node)
- 前置节点是waitStatus=Node.SIGNAL=-1 return true
- pred.waitStatus>0 任务可能被取消,自循环
node不变找到waitStatus<=0的节点,把node和pred之前满足条件的一个node链接起来 返回false
- pred.waitStatus<=0设置 pred的waitStatus=Node.SIGNA cas 返回false
- parkAndCheckInterrupt 线程挂起并且返回线程中断状态 park 并且thread.in
finally
- 这里不用看了,目前除了lock几十亿
release
1.boolean release = tryRelease(arg);
- 1.c=state-1
* 2.检查是不是当前线程持有锁,不是抛异常
* 3.检查state==0 是设置当前占用线程为null
* 4.设置state的值为c
2.释放成功unpark后继节点
-这里会把当前节点的waitStatus
//com.git.concurrent.locks.ReentrantLock.NonfairSync#lock
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
//com.git.concurrent.locks.ReentrantLock.FairSync#lock
final void lock() {
acquire(1);
}
//jdk源码考出来把if的多个步骤拆分出来
public final void acquire(int arg) {
//获取成功,返回false
boolean tryFail = tryAcquire(arg);
if(tryFail) return;
//入队
Node node = addWaiter(Node.EXCLUSIVE);
//获取到了就是false,没有获取到的线程被挂起,第二个节点会一直尝试获取,只有一种情况 线程被中断才会返回true
boolean acquireQueued = acquireQueued(node, arg);
//线程中断
if (acquireQueued) selfInterrupt();
}
1.tryAcquire
//tryAcquire(1)
//com.git.concurrent.locks.ReentrantLock.FairSync#tryAcquire
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//唯一区别多了一个hasQueuedPredecessors,如果不需要排队在进行设置,保证先来后到的顺序
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;
}
1.1 hasQueuedPredecessors
//
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
// 1.两个都为null,还没有初始化 null==null true 返回false不用排队
// 2.head==tail表示 头尾节点相同,不用排队同1
if(h == t) return false;
// 3.head为null tail不为null 直接空指针了,这种情况不会出现
Node s = h.next;
// 4.head不为null tail为null 可能正在初始化 这种情况可能在初始化直接返回true排队
if(s == null) return true;
//到了这一步队列里面起码已经有两个节点,后记节点不是当先线程操作,直接排队 返回true
return s.thread != Thread.currentThread();
}
2.addWaiter(Node.EXCLUSIVE)
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
//当前节点前一个节点是尾节点?会不会多个节点的前节点都是tail?似乎关系不大
node.prev = pred;
//把尾节点设置为当前
if (compareAndSetTail(pred, node)) {
//前尾节点,的后继节点改为当前尾节点
pred.next = node;
return node;
}
}
//尾节点为空
enq(node);
return node;
}
2.1 enq(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;
}
}
}
}
3.acquireQueued(node, arg)
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//当前节点的前置节点
final Node p = node.predecessor();
//如果前置节点是头节点,直接尝试获取,
if (p == head && tryAcquire(arg)) {
//获取成功,当前节点设置为头结点,这就是线程安全的
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//当前节点不是老二 或者获取失败 获取失败要不要挂起来,第一次返回false继续循环
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
//目前就猜到一种可能性,重入次数过大,int为负值 tryAcquire爆出异常
if (failed){
System.out.println("acquireQueued failed="+failed);
cancelAcquire(node);
}
}
}
3.1 shouldParkAfterFailedAcquire
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
System.err.println("当前线程"+Thread.currentThread().getName());
//前置节点的waitStatus
int ws = pred.waitStatus;
//如果是SIGNAL
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) {
//ws>0表示cancel,直接移除队列
do {
pred = pred.prev;
node.prev = pred;
} 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.
*/
//第一次进来把前置节点设置为-1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
3.2 parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
java.util.concurrent.locks.LockSupport.park(this);
return Thread.interrupted();
}
4. selfInterrupt
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
5.release
public final boolean release(int arg) {
boolean release = tryRelease(arg);
if(!release) return false;
//释放成功 unpark后继节点
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
5.1 tryRelease
/**
* 1.c=state-1
* 2.检查是不是当前线程持有锁,不是抛异常
* 3.检查state==0 是设置当前占用线程为null
* 4.设置state的值为c
* @param releases
* @return
*/
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
//c==0释放锁,这是其他线程可能try到了,可能是本线程或者是其他线程
setState(c);
return free;
}
5.2 unparkSuccessor
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, 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;
//后继节点为空或者是取消状态,循环找下一个节点从尾节点开始
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);
}