c 网站开发案例大全,做推广网站的文章,如何把网站提交给百度,建筑工程网站模板前置概念
要彻底了解AQS的底层实现就必须要了解一下线程相关的知识。
包括voliatevoliate 我们使用翻译软件翻译一下volatile#xff0c;会发现它有以下几个意思#xff1a;易变的;无定性的;无常性的;可能急剧波动的;不稳定的;易恶化的;易挥发的;易发散的。这也正式使用vola…前置概念
要彻底了解AQS的底层实现就必须要了解一下线程相关的知识。
包括voliatevoliate 我们使用翻译软件翻译一下volatile会发现它有以下几个意思易变的;无定性的;无常性的;可能急剧波动的;不稳定的;易恶化的;易挥发的;易发散的。这也正式使用volatile关键字的语义。 当你使用volatile去申明个变量时就等于告诉了虚拟机这个变量极有可能会被其他程序或者线程修改。为了确保这个变量被修改后应用程序范围内所有线程都能够“看到”这个改动虚拟机就必须采用一些特殊手段保证这个变量的可见性等特点 比如根据编译器的优化规则如果不使用volatile申明变量这个变量被修改后其他线程可能并不会被通知到甚至在别的线程中看到变量的修改程序都是反的。但是使用volatile虚拟机就会谨慎的处理这种情况
CAS
CAS 是高并发的一个重要的编程实现即compareAndSet对比并且设置。意思就是说要做一个修改就必须先教研我改之前的期望值是否和他现在的是否相同如果相同则修改不相同则不处理。
什么是AQS AQS的本质是java中的AbstractQueuedSynchronizer类。 AQS是并发包下的一个基类基于它实现的类包括CountDownLatchReentranLock… 下面我们就以ReentranLock为入口详细讲解下AQS public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {static final class Node {/** 用于指示节点正在共享模式下等待的标记 */static final Node SHARED new Node();/** 用于指示节点正在以独占模式等待的标记 */static final Node EXCLUSIVE null;/** waitStatus 值用于指示线程已取消 */static final int CANCELLED 1;/** waitStatus 值用于指示后续线程需要取消停放 */static final int SIGNAL -1;/** waitStatus 值用于指示线程正在等待条件 */static final int CONDITION -2;/** waitStatus 值来指示下一个 acquireShared 应无条件传播 */static final int PROPAGATE -3;/** * 状态字段仅采用以下值 * 信号此节点的后继节点被或即将阻止通过 park因此当前节点在释放或取消时必须取消其后继节点的停放。为了避免争用acquire 方法必须首先指示它们需要一个信号然后重试原子获取然后在失败时阻止。* CANCELLED该节点因超时或中断而取消。节点永远不会离开此状态。特别是具有已取消节点的线程永远不会再次阻塞。条件此节点当前位于条件队列中。在传输之前它不会用作同步队列节点此时状态将设置为 0。此处使用此值与该字段的其他用途无关但简化了机制。* 传播应将 releaseShared 传播到其他节点。这是在 doReleaseShared 中设置的仅适用于头节点以确保传播继续进行即使其他操作此后进行了干预。* 0以上都不是 为了简化使用这些值以数字形式排列。非负值表示节点不需要发出信号。因此大多数代码不需要检查特定值只需检查符号即可。对于正常同步节点该字段初始化为 0对于条件节点该字段初始化为 CONDITION。它使用 CAS 进行修改或者在可能的情况下无条件易失性写入。*/volatile int waitStatus;/*** 上一个节点*/volatile Node prev;/*** 下一个节点*/volatile Node next;/*** 将此节点排队的线程。在构造时初始化使用后清空。*/volatile Thread thread;/*** 链接到下一个节点等待条件或特殊值 SHARED。* 因为条件队列只有在以独占模式保持时才会被访问所以我们只需要一个简单的链接队列来在节点等待条件时保存它们。* 然后它们被转移到队列中以重新获取。由于条件只能是独占的因此我们通过使用特殊值来保存字段来指示共享模式。*/Node nextWaiter;/*** 如果节点在共享模式下等待则返回 true*/final boolean isShared() {return nextWaiter SHARED;}/*** 返回上一个节点如果为 null则引发 NullPointerException。* 当 predecessor 不能为 null 时使用。可以省略 null 检查但存在该检查以帮助 VM。* 返回此节点的前身*/final Node predecessor() throws NullPointerException {Node p prev;if (p null)throw new NullPointerException();elsereturn p;}Node() { // Used to establish initial head or SHARED marker}Node(Thread thread, Node mode) { // Used by addWaiterthis.nextWaiter mode;this.thread thread;}Node(Thread thread, int waitStatus) { // Used by Conditionthis.waitStatus waitStatus;this.thread thread;}}/*** 头节点*/private transient volatile Node head;/*** 尾节点*/private transient volatile Node tail;/*** 状态*/private volatile int state;
}ReentranLock
acquire /*** 以独占模式获取忽略中断。通过至少调用一次 tryAcquire来实现成功返回。* 否则线程会排队可能会反复阻塞和解除阻塞调用 tryAcquire 直到成功。此方法可用于实现方法Lock.lock。* 参数arg – acquire 参数。此值被传达给 tryAcquire 但以其他方式未解释可以表示您喜欢的任何内容。*/public final void acquire(int arg) {if (!tryAcquire(arg) acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}tryAcquire
/*** 1. 获取当前线程 * 2. 获取当前锁状态c * 3. 如果当前锁状态为0则判断是否有排队的前任线程并尝试使用compareAndSetState方法将锁状态设置为acquires如果成功则将当前线程设置为独占所有者并返回true * 4. 如果当前锁状态不为0且当前线程已经是独占所有者则计算新的锁状态nextc如果nextc小于0则抛出异常Maximum lock count exceeded否则更新锁状态为nextc并返回true * 5. 如果以上条件都不满足则返回false。*/
protected final boolean tryAcquire(int acquires) {final Thread current Thread.currentThread();int c getState();if (c 0) {if (!hasQueuedPredecessors() // 用CAS的方式设置状态compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current getExclusiveOwnerThread()) {//1. 首先代码检查当前线程是否为独占锁的拥有者如果是则执行以下操作 //2. 计算下一个锁的状态值 nextc c acquires //3. 如果下一个状态值小于0则抛出错误 Maximum lock count exceeded //4. 设置锁的状态为 nextc //5. 返回 true 表示成功获取独占锁。int nextc c acquires;if (nextc 0)throw new Error(Maximum lock count exceeded);setState(nextc);return true;}return false;
}//判断当前线程前面是否还有其他线程在等待。
public final boolean hasQueuedPredecessors() {Node t tail;Node h head;Node s;return h ! t ((s h.next) null || s.thread ! Thread.currentThread());
}
//设置线程作为独占所有者线程。
protected final void setExclusiveOwnerThread(Thread thread) {exclusiveOwnerThread thread;
}addWaiter
/*** 这段代码的作用是向一个链表中添加一个等待节点。 * 1. 创建一个新的节点将当前线程和传入的模式作为参数。 * 2. 尝试使用快速路径添加节点到链表的末尾如果失败则备用完整的添加方法。 * 3. 获取当前尾节点作为前驱节点。 * 4. 如果前驱节点不为空则设置新节点的前驱为前驱节点。 * 5. 如果成功将新节点设置为尾节点则将前驱节点的后继指向新节点然后返回新节点。 * 6. 如果无法使用快速路径则调用enq方法完整添加节点到链表末尾并返回新节点。*/
private Node addWaiter(Node mode) {Node node new Node(Thread.currentThread(), mode);// Try the fast path of enq; backup to full enq on failureNode pred tail;if (pred ! null) {node.prev pred;// 用CAS的方式设置尾部节点if (compareAndSetTail(pred, node)) {pred.next node;return node;}}enq(node);return node;
}acquireQueued
//以独占不间断模式获取已在队列中的线程。由条件等待方法以及获取使用
final boolean acquireQueued(final Node node, int arg) {boolean failed true;try {boolean interrupted false;for (;;) {// 进入一个无限循环不断尝试获取锁。 // 1.在循环中首先获取前驱节点p。 // 2. 如果p是头结点并且tryAcquire(arg)成功获取到锁则将当前节点设置为头结点断开p的next引用将failed设为false然后返回interrupted的值。 // 3. 如果无法获取锁则判断是否应该在获取失败后阻塞线程并检查是否被中断。 // 4. 如果被中断则将interrupted设为true。final Node p node.predecessor();if (p head tryAcquire(arg)) {setHead(node);p.next null; // help GCfailed false;return interrupted;}if (shouldParkAfterFailedAcquire(p, node) // 中断线程放入等待队列parkAndCheckInterrupt())interrupted true;}} finally {if (failed)// 取消竞争锁cancelAcquire(node);}
}//判断在获取锁失败后是否需要进行阻塞等待。这是所有采集环路中的主要信号控制。需要 pred node.prev。private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {//1. 首先获取前驱节点的等待状态int ws pred.waitStatus;//2. 如果等待状态(ws)等于Node.SIGNAL表示前驱节点已经设置了状态要求释放锁以发信号通知当前节点可以安全地阻塞等待返回true。 if (ws Node.SIGNAL)/** This node has already set status asking a release* to signal it, so it can safely park.*/return true;//3. 如果等待状态(ws)大于0表示前驱节点已取消需要跳过前驱节点并指示重试。在循环中将当前节点的prev指向前驱节点的prev直到找到等待状态不大于0的前驱节点然后将前驱节点的next指向当前节点。 if (ws 0) {//4. 如果等待状态(ws)为0或PROPAGATE则表示需要一个信号但暂时不阻塞等待。调用者将需要重试以确保在阻塞等待之前不能获取锁。 do {node.prev pred pred.prev;} while (pred.waitStatus 0);pred.next node;} else {// 用CAS的方式设置等待状态compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}//5. 最后返回false表示不需要阻塞等待。return false;
}