Java 同步锁ReentrantLock与抽象同步队列AQS
AbstractQueuedSynchronizer 抽象同步队列,它是个模板类提供了许多以锁相关的操作,常说的AQS指的就是它。AQS继承了AbstractOwnableSynchronizer
类,AOS用于保存线程对象,保存什么线程对象呢?保存锁被独占的线程对象。
抽象同步队列AQS除了实现序列化标记接口,并没有实现任何的同步接口,该类提供了许多同步状态获取和释放的方法给自定义同步器使用,如ReentrantLock的内部类Sync。抽象同步队列支持独占式或共享式的的获取同步状态,方便实现不同类型的自定义同步器。一般方法名带有Shared
的为共享式,比如,尝试以共享式的获取锁的方法int tryAcquireShared(int)
,而独占式获取锁方法为boolean tryAcquire(int)
。
AQS是抽象同步队列,其重点就是同步队列
及如何操作同步队列
。
同步队列
双向同步队列,采用尾插法新增节点,从头部的下一个节点获取操作节点,节点自旋获取同步锁,实现FIFO(先进先出)原则。
理解节点中的属性值作用
prev:前驱节点;即当前节点的前一个节点,之所以叫前驱节点,是因为前一个节点在使用完锁之后会解除后一个节点的阻塞状态;
next:后继节点;即当前节点的后一个节点,之所以叫后继节点,是因为“后继有人”了,表示有“下一代”节点承接这个独有的锁;
nextWaiter:表示指向下一个
Node.CONDITION
状态的节点(本文不讲述Condition队列,在此可以忽略它);thread:节点对象中保存的线程对象,节点都是配角,线程才是主角;
waitStatus:当前节点在队列中的等待状态;waitStatus = CANCELLED = 1,表示线程已经取消(该状态下的节点为作废节点,将从队列中断开);
waitStatus = SIGNAL = -1,表示线程处于请求释放的状态,后继线程需要阻塞等待(该状态下的节点线程处于阻塞等待状态或获取锁未释放状态);
waitStatus = CONDITION = -2,表示线程正在等待;
waitStatus = PROPAGATE = -3,在共享情况下,表示下一个被请求的shared应该无条件传播;
waitStatus = 0,表示节点初始化时的默认值(int类型成员变量的默认值)。
注意1:节点对象中的prev、next和nextWaiter都是一个完整的Node节点对象,也就是说每个节点都保存了前后节点的对象,如果没有则为null。
注意2:head节点是个虚节点(prev=null、thread=null),但head本身是一个实际存在的节点对象,起到标记队列的开头;尾节点tail节点的next=null,等待新节点插入。
头节点head为什么是虚节点
重点:必须明确知道头节点head为什么是虚节点!!!这很重要。
原因是,当前节点在获取到锁之后,它这个线程对象就会被保存到AOS(AbstractOwnableSynchronizer)中的exclusiveOwnerThread
对象,(一开头就提到过了,在这里再强调一次),所以在队列中,头节点是无需存储Thread对象的了。那为什么设计成这样呢?因为存在临界情况就是只有一个线程获取锁资源时,无需初始化生成同步队列,直接获取同步锁即可。只有存在锁未释放同时又进来了新的线程时,才会去初始化同步队列,并为未释放锁的线程占个位置,这个位置就是头节点head,表明前面还有个线程在使用资源。
// 初始化队列的方法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;
}
}
}
}
在独占线程释放锁时,判断head是否为null,即可知道同步队列是否存在,如果同步队列不存在,那么无需执行尝试唤醒后继节点那些操作了。
在同个时间节点中,单个线程只需要操作AOS的Thread对象和AQS的state状态即可实现同步锁和锁的可重入性。
线程加入同步队列的过程
在锁被占用时,获取锁失败后,当前线程被封装成Node节点并加入到队列尾部。
即tryAcquire(arg)
返回false时,
执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
操作,
而addWaiter(Node.EXCLUSIVE)
为新增节点到同步队列,队列未初始化时会执行enq完成初始化后再新增节点到队列。
因此,新增节点分为首次生成同步队列同时新增节点和在原有同步队列中插入新增节点。
首次生成同步队列新增节点:通过enq(final Node node)
方法,先初始化头节点(虚节点),再通过原子操作compareAndSetTail
方法从队列尾部插入新节点。
在原有同步队列中新增节点:通过原子操作compareAndSetTail
方法从队列尾部插入新节点。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
private Node addWaiter(Node mode) {
// 使用当前线程生成新节点
Node node = new Node(Thread.currentThread(), mode);
// 获取同步器的尾节点
Node pred = tail;
if (pred != null) {
// 第一步:新节点的prev节点指向尾部节点(pred=tail)
node.prev = pred;
// 第二部:CAS比较尾节点,相等就让tail=node
if (compareAndSetTail(pred, node)) {
// 第三步:pred=旧的tail,即旧的尾节点的next节点指向新节点
pred.next = node;
return node;
}
}
// 当tail=null 时执行,逻辑相类似的;enq初始化的head节点为虚节点
enq(node);
return node;
}
// 将节点插入队列,必要时初始化(即tail=null时,也即是同步队列没有节点时初始化)。private Node enq(final Node node) {
// 死循环
for (;;) {
// 第一次进来tail=null,第二次进来tail=head=new Node()
Node t = tail;
if (t == null) {
// 创建一个空节点作为head节点
if (compareAndSetHead(new Node()))
tail = head;
} else { // 下面就是正常的尾插法新增节点
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">配合源码和动图理解:新增队列节点过程(三部曲)</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">![](file://D:\Program Files\Git文档\xmindFile\01:Java\02:Java高级\多线程\images\AQS新增队列节点过程(三部曲).gif?msec=1668428554895)</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">使用尾插法新增同步队列节点</p>
<ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;">
<li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;"><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">第一步:新增节点的prev节点指向尾节点tail;</p>
</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;"><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">第二步:尾节点tail 和新节点做CAS操作,即compareAndSetTail(pred,node) ,即同步器的tail节点指向新节点;</p>
</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;"><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">第三步:旧的尾节点的next节点指向新节点(此时的新节点=尾节点tail)</p>
</section></li></ul>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">最终结果图</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">![](file://D:\Program Files\Git文档\xmindFile\01:Java\02:Java高级\多线程\images\AQS数据结构-第三步.png?msec=1668428569216)</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">新增节点加入队列之后,在同步队列中线程怎么等待?线程怎么获取锁呢?</p>
<h2 data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span class="prefix" style="display: none;"></span><span class="content" style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">节点线程获取锁</span><span class="suffix"></span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">看代码前必须明确知道哪个节点是要获取锁的。头节点为虚节点,标记队列的开头,真正要获取锁的是头节点的后继节点。</p>
<h3 data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px;"><span class="prefix" style="display: none;"></span><span class="content">获取锁过程</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">锁在释放时调用的关键流程:</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">ReentrantLock#lock() -> Sync#lock() -> AQS#acquire(1) -> NonfairSync#tryAcquire(1) 【或FairSync#tryAcquire(1)】-> AQS#addWaiter(node) -> AQS#acquireQueued(node,1) -> AQS#selfInterrupt()</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">关键代码</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br> * 以独占不可中断模式获取已在队列中的线程。<br> */</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">acquireQueued</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> Node node, <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span> arg)</span> </span>{<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 异常标志状态</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> failed = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">try</span> {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 是否发生过中断的标志</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> interrupted = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 自旋锁>>>死循环:每个node都独立执行着这个死循环,直至线程被阻塞</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">for</span> (;;) {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 获取前驱节点(当前节点的前一个节点)</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> Node p = node.predecessor();<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 当前节点的前驱节点是否等于头节点(虚节点),等于就会执行尝试获取锁 tryAcquire(arg)</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (p == head && tryAcquire(arg)) {<br> setHead(node); <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 设置当前节点为头节点,在获取锁成功时,thread对象已经保存到AQS中的exclusiveOwnerThread了</span><br> p.next = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>; <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 前驱节点的next指向null,断开前驱节点(旧的头节点)</span><br> failed = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>; <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 只要当前节点node正常获取到锁,就不会执行finally的cancelAcquire(node)</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span> interrupted;<br> }<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 前驱节点的waitStatus=-1时,当前node会被阻塞,防止无限循环浪费资源</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (shouldParkAfterFailedAcquire(p, node) &&<br> parkAndCheckInterrupt())<br> interrupted = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br> }<br> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">finally</span> {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/* 正常情况:只有return时,才会执行finally代码,而只要return,failed都等于false,<br> * 所以,failed 是为了避免节点发生异常时,node没有被释放。<br> * 比如:node.predecessor() 可能产生空指针异常。<br> */</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (failed)<br> cancelAcquire(node);<br> }<br>}<br><br><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">setHead</span><span class="hljs-params" style="line-height: 26px;">(Node node)</span> </span>{<br> head = node;<br> node.thread = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>;<br> node.prev = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>;<br>}<br></code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">为什么在尝试获取锁前要判断前驱节点是否为头节点?</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">因为除了<strong style="font-weight: bold; color: black;">阻塞被释放</strong>会让死循环继续执行的情况外,还有<strong style="font-weight: bold; color: black;">中断</strong>指令也会使线程从阻塞状态中<strong style="font-weight: bold; color: black;">被释放</strong>,所以存在任意节点提前重新执行“死循环”尝试获取锁的情况,如果不判断获取锁节点的前驱节点是否为头节点,那就会出现提前尝试获取锁,从而破坏了同步队列的先进先出(FIFO)原则,说白了,就是被插队了。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">当前节点不是头节点时,执行以下代码</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br> * 检查和更新获取锁失败的节点的状态。如果线程需要等待,则返回true,使其执行阻塞操作。<br> */</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">shouldParkAfterFailedAcquire</span><span class="hljs-params" style="line-height: 26px;">(Node pred, Node node)</span> </span>{<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 获取前驱节点的等待状态</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span> ws = pred.waitStatus;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (ws == Node.SIGNAL)<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 因为前驱节点处于请求释放的状态,所以当前节点需要阻塞等待,会返回true,从而执行后续方法进入阻塞状态</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (ws > <span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>) {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 前驱节点被标上取消标志了,需要跳过前驱节点并不断重试</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">do</span> {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 循环向前查找取消节点,把取消节点从队列中移除</span><br> node.prev = pred = pred.prev;<br> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">while</span> (pred.waitStatus > <span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>);<br> pred.next = node;<br> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">else</span> {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/*<br> * waitStatus必须为0或等于PROPAGATE=-3<br> * 表示需要设置前驱节点等待状态为SIGNAL,<br> * 将会在外层循环再次尝试获取锁,如果再次获取锁失败,那么就会阻塞当前线程<br> */</span><br> compareAndSetWaitStatus(pred, ws, Node.SIGNAL);<br> }<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;<br>}<br></code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">当<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">shouldParkAfterFailedAcquire(p, node)</code> 返回true时,将会执行阻塞操作<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">parkAndCheckInterrupt())</code>,其通过线程阻塞工具类方法<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">LockSupport.park(this)</code> 来阻塞当前线程。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">parkAndCheckInterrupt</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 阻塞当前线程线程调度</span><br> LockSupport.park(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>);<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 清除当前线程的中断状态,并返回上一次的中断状态</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span> Thread.interrupted();<br>}<br></code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">注意:<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">LockSupport.park(this)</code> 阻塞后,需要唤醒阻塞才会执行后续操作,可通过解除阻塞<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">LockSupport.unpark(thread)</code> 或 中断<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">thread.interrupt()</code> 来唤醒阻塞。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">只有<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">shouldParkAfterFailedAcquire</code> 和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">parkAndCheckInterrupt</code>都返回true时,才会执行interrupted = true,即只有是中断导致阻塞结束时,才返回true,此时 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">selfInterrupt()</code>重新执行一次中断操作。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">acquireQueued</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> Node node, <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span> arg)</span> </span>{<br> ......<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (shouldParkAfterFailedAcquire(p, node) &&<br> parkAndCheckInterrupt())<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 只有是中断导致阻塞结束时,才返回true</span><br> interrupted = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br> ......<br>}<br><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">acquire</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span> arg)</span> </span>{<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (!tryAcquire(arg) &&<br> acquireQueued(addWaiter(Node.EXCLUSIVE), arg))<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 中断当前线程</span><br> selfInterrupt();<br>}<br><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">selfInterrupt</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 中断当前线程</span><br> Thread.currentThread().interrupt();<br>}<br></code></pre>
<h3 data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px;"><span class="prefix" style="display: none;"></span><span class="content">为什么需要再一次执行中断呢?</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">因为存在中断<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">thread.interrupt()</code> 唤醒自旋锁阻塞的情况,而<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Thread.interrupted()</code> 获取中断状态并清除当前线程的中断状态,所以需要重新执行一次中断操作<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">selfInterrupt()</code>,将中断标志置为true。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">这种获取锁的方式是<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">非中断锁</code>,就是无法通过中断的方式<strong style="font-weight: bold; color: black;">结束</strong>锁的获取,区别于<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">中断锁</code>,所以该方式在获取锁的过程中,不会处理中断,只是记录中断状态,Thread.interrupted() 获取中断状态后清除中断状态,所以需要重新设置中断标志为true。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">如果你想要<strong style="font-weight: bold; color: black;">处理中断的情况</strong>,那我们可以在acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 返回true 的时候去处理。比如:抛出中断异常。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">如果你需要在<strong style="font-weight: bold; color: black;">线程发生中断时结束获取锁</strong>,那么可以考虑使用<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">lockInterruptibly()</code>来获取锁。</p>
<h3 data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px;"><span class="prefix" style="display: none;"></span><span class="content">两种方式获取锁的区别</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">lock()</code>方式获取锁:自旋锁只会在正常获取到锁或发生异常时结束自旋锁(死循环)。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">void lockInterruptibly()</code> 方式获取锁:会在发生中断的情况下,抛出中断异常<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">throw new InterruptedException();</code> 从而跳出自旋锁(死循环),而调用lockInterruptibly() 的方法需要捕获中断异常,做一些异常处理。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">获取锁的<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">lock()</code>和<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">lockInterruptibly()</code>的主要区别是:<strong style="font-weight: bold; color: black;">中断是否会结束锁的获取</strong>。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">看看源码怎么实现中断结束锁的获取</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">doAcquireInterruptibly</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span> arg)</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throws</span> InterruptedException </span>{<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> Node node = addWaiter(Node.EXCLUSIVE);<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> failed = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">try</span> {<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">for</span> (;;) {<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> Node p = node.predecessor();<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (p == head && tryAcquire(arg)) {<br> setHead(node);<br> p.next = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>; <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// help GC</span><br> failed = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>;<br> }<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (shouldParkAfterFailedAcquire(p, node) &&<br> parkAndCheckInterrupt())<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 只有是中断导致阻塞结束时,才抛出中断异常</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throw</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> InterruptedException();<br> }<br> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">finally</span> {<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (failed)<br> cancelAcquire(node);<br> }<br>}<br></code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">中断异常抛出后,将会执行finally 代码块,取消正在进行尝试获取锁的节点。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">到此为止,<strong style="font-weight: bold; color: black;">lock()获取锁的概要过程</strong>为</p>
<blockquote class="multiquote-1" data-tool="mdnice编辑器" style="border: none; display: block; font-size: 0.9em; overflow: auto; overflow-scrolling: touch; border-left: 3px solid rgba(0, 0, 0, 0.4); color: #6a737d; padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 10px; margin-bottom: 20px; margin-top: 20px; border-left-color: rgb(239, 112, 96); background: #fff9f9;">
<p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0px; color: black; line-height: 26px;">先尝试获取锁、失败就将线程封装成节点并加入到队列尾部、进入自旋锁(第一次尝试获取锁失败,将前驱节点的waitStatus改为-1;第二次尝试获取锁失败,因为前驱节点的waitStatus=-1,所以执行阻塞当前线程操作避免死循环耗费资源)、等待头节点线程释放同步状态之后,将发起解除阻塞指令或阻塞线程被中断后,后继节点再次尝试获取锁。</p>
</blockquote>
<h3 data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px;"><span class="prefix" style="display: none;"></span><span class="content">取消异常节点</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">前面提到,在AQS#shouldParkAfterFailedAcquire(pred, node) 中谈到,当节点waitStatus>0 时,也即是对带有取消状态的节点进行移除。那么节点在什么时候被改为CANCELLED 的呢?</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">在AQS#acquireQueued(node, arg)中,正常获取到锁时,failed都等于false,只有当发生异常时,failed 才等于true,从而执行到AQS#cancelAcquire(node)。也就是说cancelAcquire() 是用于处理获取锁过程中,对发生异常节点的进行移除。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">acquireQueued</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> Node node, <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span> arg)</span> </span>{<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 是否发生异常的标志</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> failed = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">try</span> {<br> ......<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">for</span> (;;) {<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> Node p = node.predecessor();<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (p == head && tryAcquire(arg)) {<br> ......<br> failed = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>; <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 只要当前节点node正常获取到锁,就不会执行finally的cancelAcquire(node)</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span> interrupted;<br> }<br> ......<br> }<br> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">finally</span> {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/* 正常情况:只有return时,才会执行finally代码,而只要return,failed都等于false,<br> * 所以,failed 是为了避免节点发生异常时,node没有被移除。<br> */</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (failed)<br> cancelAcquire(node);<br> }<br>}<br></code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">详细看下AQS#cancelAcquire(node) 是怎么处理的</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br> * 取消正在进行尝试获取锁的节点<br> */</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">cancelAcquire</span><span class="hljs-params" style="line-height: 26px;">(Node node)</span> </span>{<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 如果节点不存在,则忽略</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (node == <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>)<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>;<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 使当前节点变成虚节点</span><br> node.thread = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>;<br><br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 跳过带有“取消状态”的前驱节点</span><br> Node pred = node.prev;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">while</span> (pred.waitStatus > <span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)<br> node.prev = pred = pred.prev;<br><br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// predNext节点</span><br> Node predNext = pred.next;<br><br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 修改当前节点的waitStatus为CANCELLED=1</span><br> node.waitStatus = Node.CANCELLED;<br><br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 如果当前节点为尾节点tail,则只需要移除自己即可。</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (node == tail && compareAndSetTail(node, pred)) {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// pred节点变成了尾节点tail,所以 pred.next=null</span><br> compareAndSetNext(pred, predNext, <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>);<br> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">else</span> {<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span> ws;<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/* 当前节点的前驱节点不为头节点,则true;<br> * 前驱节点的ws状态为SIGNAL,则true;ws不为SIGNAL,但ws<=0时(即不是取消状态),则CAS操作改为SIGNAL,改成功则为true;<br> * 前驱节点不是虚接点,则true<br> */</span> <br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (pred != head<br> && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= <span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span> && compareAndSetWaitStatus(pred, ws, Node.SIGNAL)))<br> && pred.thread != <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>) {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/* 如果上述都满足,则将“当前节点的前驱节点的后继节点”指向“当前节点的后继节点”<br> * 说白了,节点的next指向就是由 A->B->C 改为 A->C;B为当前节点。<br> * 关于节点的prev指向就没有变 A<-B<-C 还是 A<-B<-C,也就是说B节点还没真正断开。<br> * 节点的prev指向的修改需要判断ws状态是否为CANCELLED后,才做修改。<br> */</span><br> Node next = node.next;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (next != <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span> && next.waitStatus <= <span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)<br> compareAndSetNext(pred, predNext, next);<br> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">else</span> {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 唤醒当前节点的后继节点的阻塞线程</span><br> unparkSuccessor(node);<br> }<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 当前节点的后继节点指向自己</span><br> node.next = node;<br> }<br>}<br></code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">以上都是对next节点的指向做修改,关于节点的prev指向的修改需要判断ws状态是否为CANCELLED后,才做修改,循环向前查找取消节点,把取消节点从队列中剔除。其对应源码如下</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">shouldParkAfterFailedAcquire</span><span class="hljs-params" style="line-height: 26px;">(Node pred, Node node)</span> </span>{<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 获取前驱节点的等待状态</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span> ws = pred.waitStatus;<br> ......<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (ws > <span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>) {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 前驱节点被标上取消标志了,需要跳过前驱节点并不断重试</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">do</span> {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 循环向前查找取消节点,把取消节点从队列中剔除</span><br> node.prev = pred = pred.prev;<br> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">while</span> (pred.waitStatus > <span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>);<br> pred.next = node;<br> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">else</span> {<br> ......<br> }<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;<br>}<br></code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">在AQS#unparkSuccessor() 通过线程阻塞工具类方法<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">LockSupport.unpark(thread)</code> 来唤醒后继节点的阻塞线程。<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">AQS#unparkSuccessor()</code> 除了<strong style="font-weight: bold; color: black;">取消异常节点</strong>时用到外,还在<strong style="font-weight: bold; color: black;">锁的释放</strong>时调用,实现功能都是--唤醒当前节点的后继节点的阻塞线程,后继节点就会继续执行自旋锁来尝试获取锁。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br> * 唤醒当前节点的后继节点的阻塞线程(如果存在)<br> */</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">unparkSuccessor</span><span class="hljs-params" style="line-height: 26px;">(Node node)</span> </span>{<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/*<br> * 如果waitStatus<0,则将waitStatus 置为默认值0<br> */</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span> ws = node.waitStatus;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (ws < <span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)<br> compareAndSetWaitStatus(node, ws, <span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>);<br><br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/*<br> * 但如果为null 或为取消状态,则从tail向前遍历以查找到实际未取消的后继节点。<br> */</span><br> Node s = node.next;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (s == <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span> || s.waitStatus > <span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>) {<br> s = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">for</span> (Node t = tail; t != <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span> && t != node; t = t.prev)<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (t.waitStatus <= <span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)<br> s = t;<br> }<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 对后继节点的线程释放阻塞</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (s != <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>)<br> LockSupport.unpark(s.thread);<br>}<br></code></pre>
<h2 data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span class="prefix" style="display: none;"></span><span class="content" style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">节点线程释放锁</span><span class="suffix"></span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">获取锁搞懂后,释放锁就是很简单了</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;"><strong style="font-weight: bold; color: black;">处理流程</strong></p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">锁在释放时调用的关键流程:ReentrantLock#unlock() -> Sync#release(1) -> AQS#tryRelease(1) -> LockSupport#unparkSuccessor(head)</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">unparkSuccessor(head)方法在前面已经讲述过不再赘述,前三个方法源码如下</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// java.util.concurrent.locks.ReentrantLock</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">unlock</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// ReentrantLock API 交由同步队列模板方法实现</span><br> sync.release(<span class="hljs-number" style="color: #d19a66; line-height: 26px;">1</span>);<br>}<br><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">release</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span> arg)</span> </span>{<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 尝试释放锁,成功则唤醒后继节点</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (tryRelease(arg)) {<br> Node h = head;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (h != <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span> && h.waitStatus != <span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 唤醒当前节点的后继节点的阻塞线程</span><br> unparkSuccessor(h);<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br> }<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;<br>}<br><br><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// java.util.concurrent.locks.ReentrantLock.Sync</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">protected</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">tryRelease</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span> releases)</span> </span>{<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// state:每释放1次锁就会-1(相反:重入性,每获取1次锁就会+1)</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span> c = getState() - releases;<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 当前线程是否是独占锁线程,不是就抛出异常</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (Thread.currentThread() != getExclusiveOwnerThread())<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throw</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> IllegalMonitorStateException();<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> free = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 当state=0时,说明获取锁的次数已经释放完,可以解除独占锁线程</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (c == <span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>) {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 锁释放成功</span><br> free = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 独占锁线程置为null</span><br> setExclusiveOwnerThread(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>);<br> }<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 记录每次state的变化</span><br> setState(c);<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span> free;<br>}<br></code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">最后附上以 ReentrantLock 的<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">lock()</code>为例,里面几乎画出了获取锁的所有代码的执行过程</p>
</section>
Java 同步锁ReentrantLock与抽象同步队列AQS的更多相关文章
- 同步锁——ReentrantLock
本博客系列是学习并发编程过程中的记录总结.由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅. 并发编程系列博客传送门 Lock接口简介 在JUC包下面有一个java.util ...
- java中多线程模拟(多生产,多消费,Lock实现同步锁,替代synchronized同步代码块)
import java.util.concurrent.locks.*; class DuckMsg{ int size;//烤鸭的大小 String id;//烤鸭的厂家和标号 DuckMsg(){ ...
- Java同步锁全息详解
一 同步代码块 1.为了解决并发操作可能造成的异常,java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块.其语法如下: synchronized(obj){ // ...
- Spring @Transactional注解和ReentrantLock同步锁同时使用不能同步的问题
结论:如果在service层的方法上同时使用事务和同步锁无法保证数据同步. 1 @Service 2 public class ServiceImpl{ 3 4 private static Lock ...
- Java synchronized对象级别与类级别的同步锁
Java synchronized 关键字 可以将一个代码块或一个方法标记为同步代码块.同步代码块是指同一时间只能有一个线程执行的代码,并且执行该代码的线程持有同步锁.synchronized关键字可 ...
- 深入理解java内置锁(synchronized)和显式锁(ReentrantLock)
多线程编程中,当代码需要同步时我们会用到锁.Java为我们提供了内置锁(synchronized)和显式锁(ReentrantLock)两种同步方式.显式锁是JDK1.5引入的,这两种锁有什么异同呢? ...
- Lock同步锁--线程同步
Lock-同步锁 Lock是java5提供的一个强大的线程同步机制--通过显示定义同步锁对象来实现同步.Lock可以显示的加锁.解锁.每次只能有一个线程对lock对象加锁. Lock有ReadLock ...
- 线程同步 synchronized 同步代码块 同步方法 同步锁
一 同步代码块 1.为了解决并发操作可能造成的异常,java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块.其语法如下: synchronized(obj){ // ...
- Java 之 线程安全(线程同步)
一.线程安全 当有多个线程同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,这就是线程安全的. 下面通过一个案例来演示线程的 ...
- AQS 自定义同步锁,挺难的!
AQS是AbstractQueuedSynchronizer的简称. AbstractQueuedSynchronizer 同步状态 AbstractQueuedSynchronizer 内部有一个s ...
随机推荐
- qt unknown type name编译报错记录
在classA中include class B,然后定义成员变量的时候,报错unknown type name了. 一共有两种可能造成这种问题: 1.circle include,同时在classA中 ...
- Redis图形化管理工具
一.treeNMS Redis做为现在web应用开发的黄金搭担组合,工作中的项目大量使用了Redis,treeNMS是一款用于JAVA语言开发的Redis管理工具:treeNMS管理工具,直接到htt ...
- HiveSql调优系列之Hive严格模式,如何合理使用Hive严格模式
目录 综述 1.严格模式 1.1 参数设置 1.2 查看参数 1.3 严格模式限制内容及对应参数设置 2.实际操作 2.1 分区表查询时必须指定分区 2.2 order by必须指定limit 2.3 ...
- KingbaseES 数据库静默安装
关键字:KingbaseES.V8R6.Silent.Java 一.环境准备 1.硬件环境支持 金仓数据库管理系统KingbaseES支持X86.X86_64,同时支持龙芯.飞腾等国产CPU硬件体系结 ...
- 手写tomcat——编写一个echo http服务器
核心代码如下: public class DiyTomcat1 { public void run() throws IOException { ServerSocket serverSocket = ...
- 【读书笔记】C#高级编程 第九章 字符串和正则表达式
(一)System.String类 System.String是一个类,专门用于存储字符串,允许对字符串进行许多操作.C#提供了关键字string和相关的语法,以便使用这个类更轻松. 例子: 使用运算 ...
- Windows编程之线程同步
本笔记整理自:<Windows核心编程(第五版)> 目录 什么是线程同步 用户方式中的线程同步 原子访问:Interlocked系列函数 CRITICAL_SECTION:关键段 内核对象 ...
- PLSQL Developer安装详细步骤,小白,转发
下载软件可以直接在百度网盘里面下载 链接:https://pan.baidu.com/s/1bZNJ71d2-hvkM6PTbdpgAA 提取码:t9sh 然后直接参考这个链接进行安装https:// ...
- 2020年12月-第02阶段-前端基础-CSS Day06
CSS Day06 定位(position) 理解 能说出为什么要用定位 能说出定位的4种分类 能说出四种定位的各自特点 能说出我们为什么常用子绝父相布局 应用 能写出淘宝轮播图布局 1. CSS 布 ...
- 2022-9-5 JavaSE note
Java SE 1.IDEA基本操作 psvm + 回车 : main() 方法声明 sout + 回车 : = System.out.println(); Ctrl + D : 把当前行复制到下一行 ...