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);
}
}
while (state.get()){}
是一個改進,效果是先看一眼lock的狀態,當lock是false時,
再真正的執行state.getAndSet(true)
。- 當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,當釋放鎖時通知
後面的線程。