【JUC源码】JUC核心:AQS(二)同步队列源码分析(独占锁)

论坛 期权论坛     
选择匿名的用户   2021-5-31 08:44   149   0
<p> </p>
<blockquote>
<p>AQS 系列:</p>
<ul><li><a href="https://blog.csdn.net/weixin_43935927/article/details/108693296">【JUC源码】JUC核心:AQS(一)底层结构分析</a></li><li><a href="https://blog.csdn.net/weixin_43935927/article/details/113901484">【JUC源码】JUC核心:AQS(二)同步队列源码分析(独占锁)</a></li><li><a href="https://blog.csdn.net/weixin_43935927/article/details/113904384">【JUC源码】JUC核心:AQS(三)同步队列源码分析(共享锁)</a></li><li><a href="https://blog.csdn.net/weixin_43935927/article/details/113901502">【JUC源码】JUC核心:AQS(四)条件队列源码分析</a></li><li><a href="https://blog.csdn.net/weixin_43935927/article/details/108699927">【JUC源码】JUC核心:关于AQS的几个问题</a></li></ul>
</blockquote>
<p>同步队列:</p>
<ul><li>作用:管理多个线程的休眠与唤醒</li><li>策略:可以执行的线程 &#61; RUNNABLE 状态 &amp;&amp; tryAcquire() 成功
  <ul><li>独占模式(EXCLUSIVE):队首持锁,唤醒队二后 tryAcquire() 尝试拿锁,队三及以后休眠</li><li>共享模式(SHARED):相较于独占模式只唤醒队二 ,共享模式还唤醒所有 mode&#61;shared 节点(多了一步)<br> 注:这里需要明确一点,独占和共享是<strong>对于加锁而言</strong>(能否多线程同时获锁),释放锁时没有独占和共享的概念</li></ul></li><li>状态
  <ul><li>初始化(0):入队的初始状态</li><li>SIGINAL(-1):若当前 node 后面还有 node,就要从 0-&gt;SIGNAL</li><li>CANCELLED(1):拿锁失败或出现异常,会在置为 CANCELLED 后删除,但并发时可能会暂时维持</li></ul></li></ul>
<h2><a name="t0"></a><a id="1_19"></a>1.独占-加锁</h2>
<h3><a name="t1"></a><a id="acquire_20"></a>acquire()</h3>
<p>该方法用作获取锁。排他模式下,acquire 方法由子类的 lock 方法直接调用。如下图是 Reentrantlock 的静态内部类 Sync 和 NonfairSync:</p>
<p><img alt="在这里插入图片描述" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-3818b7d97c723bd3b1f55d5511ca83c6"></p>
<p><img alt="在这里插入图片描述" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-b3a10e5ded6c29171784866187e82c21.png"><br> 注:从图中也可以看出 NonfairSync 的 lock 方法是<strong>非公平</strong>的,因为当前线程直接就有获取锁的机会(CAS修改state成功),不是必须要进入同步队列,接受同步器的调度。</p>
<ul><li>尝试获得锁,成功直接放回</li><li>失败,加入同步队列,等待拿锁</li></ul>
<pre class="blockcode"><code>public final void acquire(int arg) {
    // tryAcquire 方法是需要子类去实现的
    // CAS修改state判断能否拿到锁,拿到锁return true,不会再进入addWaiter
    if (!tryAcquire(arg) &amp;&amp;
        // addWaiter 入参代表是排他模式
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
</code></pre>
<blockquote>
<p>PS:这里一定要明白,AQS 中并没有实现 tryAcquire() 方法,它是交由子类去实现的,因为它是使用 AQS 加锁的关键。</p>
</blockquote>
<p>如下图,是 Reentrantlock中 NonfairSync 的 tryAcquire 方法的具体实现</p>
<p><img alt="在这里插入图片描述" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-38273bd24ba87100688f2e9740d0f0b6"></p>
<pre class="blockcode"><code>// Reentrantlock.sync#nonfairTryAcquire()
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
    final Thread current &#61; Thread.currentThread();
    // 获取当前同步器的状态
    int c &#61; getState();
    // 若c&#61;0,表示没有线程持锁,即有机会获取锁
    if (c &#61;&#61; 0) {
     // 尝试将state通过CAS设置为1
        if (compareAndSetState(0, acquires)) {
         // 若CAS成功,表示当前线程可以拿锁,则将拿锁线程设为当前
            setExclusiveOwnerThread(current);
            return true; // 返回true
        }
    }
    // 如果当前线程已经获得锁了
    else if (current &#61;&#61; getExclusiveOwnerThread()) {
      // 重入,&#43;1
        int nextc &#61; c &#43; acquires;
        if (nextc &lt; 0) // overflow
            throw new Error(&#34;Maximum lock count exceeded&#34;);
        setState(nextc);
        // 返回true
        return true;
    }

// 否则,返回false,表示获取不到锁
    return false;
}
</code></pre>
<h3><a name="t2"></a><a id="addWaiter_80"></a>addWaiter()</h3>
<p>回到 acquire() ,可以看到 tryAcquire() 后,就是 addWaiter(),将等待的线程加入同步队列。</p>
<blockquote>
<p>PS:这里注意一点,对于同步队列节点的所有操作,都要是线程安全的,即通过 CAS</p>
</blockquote>
<pre class="blockcode"><code>private Node addWaiter
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP