final void lock() {acquire(1);
}
调用lock方法时实际调用的是acquire()方法
public final void acquire(int arg) {//tryAcquire(arg)尝试去获取锁if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}
tryAcquire()获取锁成功,则加锁
获取锁失败,则addWaiter()将所有竞争锁失败的线程入队,并且调用acquireQueued()方法
protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();//拿到当前线程状态if (c == 0) {//如果未加锁则判断前面是否有其他线程,无则用cas方法加锁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;}//加锁失败返回falsereturn false;}
}private Node addWaiter(Node mode) {Node node = new Node(Thread.currentThread(), mode);Node pred = tail;//如果尾节点不为null,执行快速入队if (pred != null) {node.prev = pred;if (compareAndSetTail(pred, node)) { = node;return node;}}//如果为null或者入队失败,走完整入队逻辑enq(node);return node;}private Node enq(final Node node) {for (;;) {//自旋入队,将所有节点入队Node t = tail;//如果尾节点为null,说明当前队列是空的,则需要帮持锁线程在队列中补充一个节点if (t == null) { // Must initializeif (compareAndSetHead(new Node()))tail = head;} else {//其他节点入队逻辑node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}}}
//入队完以后,走acquireQueued方法
//acquireQueued方法做的事情就是将节点挂起 或者 节点被唤醒之后的逻辑
final boolean acquireQueued(final Node node, int arg) {//true,表示当前线程是被异常或者中断的,需要执行finally代码块里面的方法//false,表示需要出队,不会执行finally里面的方法boolean failed = true;try {boolean interrupted = false;for (;;) {final Node p = node.predecessor();//如果当前节点前置节点为头节点,并且释放锁,当前节点就去抢占锁,成功就返回中断值if (p == head && tryAcquire(arg)) {setHead(node);//帮助老的头节点出队p.next = null; // help GCfailed = false;return interrupted;}//若前置节点不为头节点,则挂起//若前置节点状态为signal,则需要挂起if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}
}
//挂起逻辑,加上上一个方法的自旋,就算前置节点状态不为signal,最后也会被设置为signal,并且cancel节点会被跳过,实则就出队了
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;//判断前置节点状态是否为signalif (ws == Node.SIGNAL)return true;//条件成立,代表当前节点的前置节点为cancel状态,需要出队if (ws > 0) {//帮助当前节点找到前置节点状态不为cancel状态的节点do {node.prev = pred = pred.prev;} while (pred.waitStatus > 0); = node;} else {//找到了则将前置节点设置为signalcompareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;
}//挂起当前线程,并看看当前线程要不要中断
private final boolean parkAndCheckInterrupt() {LockSupport.park(this);return Thread.interrupted();}
public void unlock() {lease(1);
}
unlock其实是调用了release方法
public final boolean release(int arg) {//尝试释放锁if (tryRelease(arg)) {//释放锁成功Node h = head;//判断当前队列是否为空,如果头节点不为空,说明队列已经创建//再判断当前头节点状态是否为0,不为0说明头节点的后继节点修改过此节点的状态if (h != null && h.waitStatus != 0)//唤醒下一个线程unparkSuccessor(h);return true;}return false;
}
//前置节点也就是node释放锁成功,唤醒下一个节点的逻辑如下
private void unparkSuccessor(Node node) {int ws = node.waitStatus;//如果前置节点状态为signal,则修改为0,表明已经唤醒了下一个节点了if (ws < 0)compareAndSetWaitStatus(node, ws, 0);//获取后置节点Node s = ;//后置节点为null,表明没有需要唤醒的节点了(两钟情况为null,一是当前节点为尾节点,二是入队的时候给打断了)//后置节点不为空但是后置节点为cancel状态,不需要唤醒,因为他需要出队(在那个方法里出队了)//如果符合上述两个条件之一,说明需要找到当前节点的下一个可以被唤醒的节点,也就是状态为signalif (s == null || s.waitStatus > 0) {s = null;//for (Node t = tail; t != null && t != node; t = t.prev)if (t.waitStatus <= 0)s = t;}//如果下个节点不为空且状态是signal,则唤醒if (s != null)LockSupport.unpark(s.thread);
}
//当前节点取消竞争,首先需要拿到当前节点的前置节点不为cancel的节点
private void cancelAcquire(Node node) {if (node == null)return;node.thread = null;//拿到需要取消排队节点的前置节点Node pred = node.prev;//如果前置节点的状态大于0,说明是cancel状态//那么就一直去找前置节点不是cancel的节点while (pred.waitStatus > 0)node.prev = pred = pred.prev;//1.predNext为当前节点node//2.predNext为Wc>0的节点,即cancel节点,因为上一步找前置节点为不是cancel节点的时候跳过了cacel节点//但是没有把前置节点的后继指针修改Node predNext = ;//将当前节点的状态设置为cancel表示出队node.waitStatus = Node.CANCELLED;/*** 当前取消排队的node所在 队列的位置不同,执行的出队策略是不一样的,一共分为三种情况:* 1.当前node是队尾 tail -> node* 2.当前node 不是 节点,也不是 tail* 3.当前node 是 节点。*///如果当前节点为尾节点执行以下操作if (node == tail && compareAndSetTail(node, pred)) {compareAndSetNext(pred, predNext, null);} else {//如果当前节点不是并且也不是tail节点,则执行以下操作int ws;//if条件块里面就是保证前置节点的状态为signalif (pred != head &&((ws = pred.waitStatus) == Node.SIGNAL ||(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&pred.thread != null) {//这里是出队逻辑Node next = ;if (next != null && next.waitStatus <= 0)compareAndSetNext(pred, predNext, next); }// 如果当前节点是节点else {unparkSuccessor(node);} = node; // help GC}
}
本文发布于:2024-02-01 23:46:19,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170680728839947.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |