幾種鎖算法的實現



Multicore

Abstract

4種Lock的實現:

  • TASLock
  • TTASLock
  • CLHLock
  • MCSLock

TASLock

每一個Lock帶有一個狀態位,lock()與unlock()操作原子的改變狀態位。
false時可進入,true時spin。

public class TASLock implements Lock
{
    AtomicBoolean state = new AtomicBoolean(false);
    public void lock()
    {
        while(state.getAndSet(true))
        {}
    }

    public void unlock()
    {
        state.set(false);
    }
}

defect:

  • 在鎖被其他線程持有的情況下,while(state.getAndSet(true))會不停的將
    state從true改爲true

TTASLock

TASLock算法的改進。

public class TTASLock implements Lock()
{
    AtomicBoolean state = new AtomicBoolean(false);
    public void lock()
    {
        while (true)
        {
            while (state.get())
            {};
            if (! state.getAndSet(true))
                return;
        }
    }

    public void unlock()
    {
        state.set(false);
    }
}
  1. while (state.get()){}是一個改進,效果是先看一眼lock的狀態,當lock是false時,
    再真正的執行state.getAndSet(true)
  2. 當state.getAndSet(true) 的return爲false時,說明之前的確是false,於是獲得鎖,return。
    否則回到while(true),再次嘗試獲得鎖。
defect:
  • 在unlock時,state.set(false)還是會帶來大量的cache miss。
  • cache miss VS cache hit

CLHLock

隊列鎖。
CLHLock

void initCLHlock()
{
    q.locked = FALSE;
    tail = &q;
}

void lock()
{

    QNode* qnode = (QNode*)pthread_getspecific(myNode);
    qnode->locked = TRUE;
    QNode* pred = getAndSet(qnode);//原子的得到隊尾,並將qnode設爲新的隊尾。
    pthread_setspecific(myPred, pred);


    while(pred->locked)
    {
    }

}


void unlock()
{
    QNode* qnode = (QNode*)pthread_getspecific(myNode);
    qnode->locked = FALSE;
    QNode* pred = (QNode*)pthread_getspecific(myPred);
    pthread_setspecific(myNode, pred);//unlock時必須將myNode指向前面的Node
}
NOTE:
  • unlock時必須將myNode指向前面的Node!
    > 後果:如果Thread A unlock()後,緊接着又進入
    隊尾, A的locked會再次被置爲TRUE, Thread B還在看着Thread A 的locked字段,於是產生
    deadlock。
  • 初始時教室裏面有一個空凳子, 每個學生來到門口排隊時都自己帶着一個凳子。

MCSLock

public class MCSLock implements Lock
{
    AtomicReference<QNode> tail;
    ThreadLocal<QNode> myNode;
    public MCSLock()
    {
        queue = new AtomicReference<QNode>(null);
        myNode = new ThreadLocal<QNode>()
        {
            protected QNode initialValue()
            {
                return new QNode();
            }
        };
    }
    ...
    class QNode
    {
        boolean locked = false;
        QNode next = null;//與CLHLock相比,多了這個真正的next
    }
}   

public void lock()
{
    QNode qnode = myNode.get();
    QNode pred = tail.getAndSet(qnode);
    if (pred != null)
    {
        qnode.locked = true;
        pred.next = qnode;
        //wait until predecessor gives up the lock
        while(qnode.locked){}//將自己設爲true然後spin,看似deadlock
    }
}
public void unlock()
{
    QNode qnode = myNode.get();
    if (qnode.next == null)         //後面沒有等待線程的情況
    {//------there is a gap!!!!
        if (tail.compareAndSet(qnode, null))
            return;                 //真的沒有等待線程,則直接返回,不需要通知
        //wait until predecessor fills in its next field
        while (qnode.next == null){}
    }
    //右面有等待線程,則通知後面的線程
    qnode.next.locked = false;
    qnode.next = null;
}
NOTE:
  • unlock()要要特別的注意。

Summary

  • CLHLock的思想是當前線程在前一個線程的node上spin,每個線程unlock時修改自身的標記。
    在共享總線結構下性能可以,無法應對分佈式。
  • MCSLock 用於解決分佈式並行的問題。每個線程都在自己的node上spin,當釋放鎖時通知
    後面的線程。
發佈了3 篇原創文章 · 獲贊 7 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章