簡體   English   中英

MCS鎖實現中的問題-JAVA

[英]Problems in MCS Lock Implementation - JAVA

我編寫了以下代碼(摘自《多處理器編程的藝術》一書):

package Chapter7;

import java.util.concurrent.atomic.AtomicReference;

public class MCSLock implements Lock {

    AtomicReference<QNode> tail;
    ThreadLocal<QNode> myNode;

    public MCSLock() {
        tail = new AtomicReference<>(null);
        myNode = new ThreadLocal<QNode>() {
            @Override
            protected QNode initialValue() {
                return new QNode();
            }
        };
    }

    @Override
    @SuppressWarnings("empty-statement")
    public void lock() {
        QNode qnode = myNode.get();
        QNode pred = tail.getAndSet(qnode);
        if (pred != null) {
            qnode.locked = true;
            pred.next = qnode;
            while (qnode.locked);   // line A
        }
    }

    @Override
    @SuppressWarnings("empty-statement")
    public void unlock() {
        QNode qnode = myNode.get();
        if (qnode.next == null) {
            if (tail.compareAndSet(qnode, null)) {
                return;
            }
            while (qnode.next == null);    // line B
        }
        qnode.next.locked = false;
        qnode.next = null;
    }

    class QNode {
        boolean locked = false;
        QNode next = null;
    }
}

如果我使用少量線程和操作進行測試,這似乎可行,但是每次嘗試使用此鎖保護的每個線程使用8個線程和1000個操作時,它都會進入死鎖狀態。 我插入了一些打印來調試代碼,並插入了另一個線程來收集工作線程中的數據。 我找到:

  • 有時僵局在A行上,有時在B行上。
  • 在第一種情況下,所有線程都在行A上循環。另一個正在收集數據的線程表明,線程在其上循環的變量全部為true,但是一個,因此一個線程應該有可能取得進展!
    • 在第二種情況下,除了一個線程外,所有線程都在A行上循環,另一個線程在B行上。數據收集線程顯示qnode.next不為null。
    • 因為我檢查了它們正在“活動等待”中插入簡單的計數器(並且它們正在增加),所以沒有飢餓的線程。

該測試在簡單的PriorityQueue上完成。

此代碼的問題在於,它嘗試使用ThreadLocal來實現線程限制。 但是,由於在鏈接列表中鏈接了QNodes並通過next引用和tail引用來操縱實例,這破壞了線程的限制,並且在QNode字段上沒有其他同步機制的情況下,不能保證更改在線程之間的可見性。

盡管qnode.next.locked = false; ,但繼續在A行循環是看到過時值的結果qnode.next.locked = false; 調用unlock()

類似地,盡管pred.next = qnode;仍然繼續在B行循環是看到過時的值的pred.next = qnode; 調用lock()

在這兩種情況下,另一個線程的QNode的字段QNode發生突變。 在前一種情況下, qnode.next ,在后一種情況下, pred是另一個線程的QNodes

如果在線程 pred.next中運行set pred.next ,然后在不同步的qnode.next線程 qnode.next中檢查qnode.next ,則由於內存屏障線程_2可能永遠不會獲得您在線程_1中設置的值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM