溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JDK里的自旋鎖怎么用

發(fā)布時間:2021-12-17 13:38:59 來源:億速云 閱讀:122 作者:小新 欄目:大數(shù)據(jù)

這篇文章主要為大家展示了“JDK里的自旋鎖怎么用”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“JDK里的自旋鎖怎么用”這篇文章吧。

自旋鎖是采用讓當(dāng)前線程不停地的在循環(huán)體內(nèi)執(zhí)行實現(xiàn)的,當(dāng)循環(huán)的條件被其他線程改變時才能進(jìn)入臨界區(qū)。

JDK里面自旋鎖的實現(xiàn)有 SynchronousQueue  和 LinkedTransferQueue。  

先說公平鎖,先等待的線程先獲得數(shù)據(jù)。SynchronousQueue的內(nèi)部類TransferQueue實現(xiàn)了公平鎖。

某一時刻 線程A看到內(nèi)存的情況如下:   鏈表,head 和 tail 分別指向鏈?zhǔn)缀玩溛玻⑶揖€程執(zhí)行了ht = tail 。

*
*  head            ht=tail
*    |               |
*    v               v*    M ->  U -> U -> U*

M 代表match , 表示該節(jié)點已經(jīng)匹配到了數(shù)據(jù),  poll 等到了 offer ,或者 offer 等到了 poll 。

U 代表unmatch , 表示該節(jié)點在等待填充數(shù)據(jù)或者數(shù)據(jù)等待poll 。

由于并發(fā)操作,經(jīng)過其他線程的操作使得鏈表變成如下:(此操作是瞬間完成的)

*   head            ht      tail
*    |              |         |
*    v              v         v
*    M -> M -> U -> U -> U -> U*

有兩個U節(jié)點添加進(jìn)來, 第二個節(jié)點M匹配到了數(shù)據(jù)。

假設(shè)此時線程A想再往鏈表添加U節(jié)點:

那么需要進(jìn)行那些操作?

1: 發(fā)現(xiàn)ht != tail , 于是需要繼續(xù)循環(huán)

2:在 ht == tail 的情況下需要先 判斷 tail.next 是否為null , 如果不是則繼續(xù)循環(huán)

3:新建一個node,將 tail.next 指向新node。

4:將U賦值給tail 。    (如果只執(zhí)行了3而還沒有執(zhí)行4,就會出現(xiàn)2中的情況,ht=tail,但是ht.next 為空,說明此時有線程執(zhí)行了3操作但是還沒有執(zhí)行4操作,此時只需要繼續(xù)循環(huán)即可)

*  head           ht=tail*    |              |*    v              v*    U -> U -> U -> U -> U
/* *   head                tail *    |                   | *    v                   v
 *    U -> U -> U -> U -> U
 * */

5:  自旋等待match。

6: 等待結(jié)束有兩種可能結(jié)果: 6.1 超時取消    6.2 成功  

成功時: 因此只需將 head 指向node.next . 并且將 node.next 置空。 雖然此時   M -> M  但是只要其它線程正確的執(zhí)行了M.next = null 即可。

/* *                  head                    tail *                   |                        | *                   v                        v
 *    M  M  M  M  M  M -> M -> U -> U - > U ->U
 * *//*
 *                           head          tail
 *                            |              |
 *                            v              v
 *    M  M  M  M  M  M ->  M  U -> U - > U ->U
 *
 */

等待超時:此時U按理也應(yīng)該來到了鏈?zhǔn)?,前面node的也都超時了,但萬一其他的線程沒有獲得cpu呢,就會出現(xiàn)如下的狀況,需要將U前面的幾個node也順便清理掉

/* *                  head                    tail *                   |                        | *                   v                        v *    M  M  M  M  M  U -> U -> U -> U - > U ->U * */

假設(shè)此時線程A是想將U變成M,這個邏輯很簡單,按順序找到一個U,嘗試給U的item賦值。成功結(jié)束,不成功繼續(xù)循環(huán)。

E transfer(E e, boolean timed, long nanos) {    QNode s = null; // constructed/reused as needed    boolean isData = (e != null);    for (;;) {
        QNode t = tail;        QNode h = head;        if (t == null || h == null)         // saw uninitialized value            continue;                       // spin        if (h == t || t.isData == isData) { // empty or same-mode            QNode tn = t.next;            if (t != tail)                  // inconsistent read                continue;            if (tn != null) {               // lagging tail                advanceTail(t, tn);                continue;            }if (timed && nanos <= 0)        // can't wait                return null;            if (s == null)
                s = new QNode(e, isData);            if (!t.casNext(null, s))        // failed to link in                continue;            advanceTail(t, s);              // swing tail and wait            Object x = awaitFulfill(s, e, timed, nanos);            if (x == s) {                   // wait was cancelled                clean(t, s);                return null;            }if (!s.isOffList()) {           // not already unlinked                advanceHead(t, s);          // unlink if head                if (x != null)              // and forget fields                    s.item = s;                s.waiter = null;            }return (x != null) ? (E)x : e;        } else {                            // complementary-mode            QNode m = h.next;               // node to fulfill            if (t != tail || m == null || h != head)continue;                   // inconsistent read            Object x = m.item;            if (isData == (x != null) ||    // m already fulfilled                x == m ||                   // m cancelled                !m.casItem(x, e)) {         // lost CAS                advanceHead(h, m);          // dequeue and retry                continue;            }

            advanceHead(h, m);              // successfully fulfilled            LockSupport.unpark(m.waiter);            return (x != null) ? (E)x : e;        }
    }
}

自旋分析:

Object awaitFulfill(QNode s, E e, boolean timed, long nanos) {/* Same idea as TransferStack.awaitFulfill */    final long deadline = timed ? System.nanoTime() + nanos : 0L;    Thread w = Thread.currentThread();    int spins = ((head.next == s) ?
                 (timed ? maxTimedSpins : maxUntimedSpins) : 0);    for (;;) {if (w.isInterrupted())
            s.tryCancel(e);        Object x = s.item;        if (x != e)return x;        if (timed) {
            nanos = deadline - System.nanoTime();            if (nanos <= 0L) {
                s.tryCancel(e);                continue;            }
        }if (spins > 0)
            --spins;        else if (s.waiter == null)
            s.waiter = w;        else if (!timed)
            LockSupport.park(this);        else if (nanos > spinForTimeoutThreshold)
            LockSupport.parkNanos(this, nanos);    }
}

循環(huán)判斷 node.item 有沒有變化,如果有變化則匹配成功,如果沒有則繼續(xù)循環(huán), 循環(huán)一定的次數(shù)(spins)后還沒有匹配成功則使用 LockSupport.park() 來阻塞線程。這個循環(huán)過程即可稱為自旋。

但是公平鎖存在一個問題:

/* * *  head            ht=tail *    |               | *    v               v *    M ->  U -> U -> U *   
 *   | *   | *  U變成M,但是此線程并沒有獲得cpu,沒法立即執(zhí)行,于是后面獲得cpu的線程便有了意見,為啥不將數(shù)據(jù)給我,我能立即執(zhí)行。 */

要想解決此問題說來也十分簡單,每個線程只需不停的將自己的node和head進(jìn)行交換(casHead)即可優(yōu)先獲得task。 這個是在 TransferStack中實現(xiàn)的,說起來簡單,但實現(xiàn)起來則困難得多。其實這也是經(jīng)常聽到的搶占式執(zhí)行吧,SynchronousQueue 默認(rèn)使用非公平鎖。

public SynchronousQueue() {this(false);}public SynchronousQueue(boolean fair) {transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();}

以上是“JDK里的自旋鎖怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

jdk
AI