學而時習之,不亦說乎!
--《論語》
原創,轉載請附原文鏈接,謝謝。
CLH
思路
- 保持時序的鎖基本思路就是將等待獲取鎖的線程放入集合,鎖釋放后,等待線程之一獲取到鎖。
問題
-
如何排隊?
- CLH使用反向鏈表的形式進行排隊。也就是后繼節點主動詢問,而不是前繼節點主動通知。
-
排隊是否公平?
- CLH是公平鎖。即后申請獲取鎖的排在隊列末尾。
-
如何喚醒?
- CLH通過每個線程自旋。每個等待線程通過不斷自旋前繼節點狀態判斷是否能獲取到鎖。
實現
- CLH排隊使用公平方式,鎖內部需要保存【尾節點】的引用,每次排隊線程加入到隊列最末尾。
- CLH鎖使用反向鏈表方式進行排隊,那么每個線程就需要維護【自己的狀態】和保持一個【前向節點的引用】。
- CLH使用一個【boolean值】表示當前線程獲取鎖狀態。false表示當前線程釋放鎖;true表示當前線程等待獲取鎖或已經獲取到鎖。
代碼
Lock.java
xxxxxxxxxx
package com.zby.clh;
public interface Lock {
void lock();
void unlock();
}
CLHLock.java
xxxxxxxxxx
package com.zby.clh;
import java.util.concurrent.atomic.AtomicReference;
public class CLHLock implements Lock {
private final AtomicReference<QNode> tail;
private final ThreadLocal<QNode> myPred;
private final ThreadLocal<QNode> myNode;
public CLHLock() {
tail = new AtomicReference<QNode>(new QNode());
myNode = new ThreadLocal<QNode>() {
protected QNode initialValue() {
return new QNode();
}
};
myPred = new ThreadLocal<QNode>() {
protected QNode initialValue() {
return null;
}
};
}
public void lock() {
QNode qnode = myNode.get();
qnode.locked = true;
QNode pred = tail.getAndSet(qnode);
myPred.set(pred);
while (pred.locked) {
}
}
public void unlock() {
QNode qnode = myNode.get();
qnode.locked = false;
// myNode.set(myPred.get());這一行是否有必要???
}
private static class QNode {
public volatile boolean locked;
}
}
測試
xxxxxxxxxx
package com.zby.clh;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class CLHMain {
private Lock lock = new CLHLock();
public void say(String msg) {
System.out.println(Thread.currentThread().getName() + " : 嘗試獲取鎖");
lock.lock();
System.out.println(Thread.currentThread().getName() + " : 獲取到鎖");
try {
System.out.println(Thread.currentThread().getName() + msg + "?");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + msg + "!");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + msg + ".");
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + " : 釋放鎖");
}
}
public static void main(String[] args) throws Exception {
String msg = "Hello,World";
CLHMain clhMain = new CLHMain();
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
executorService.submit(new Runnable() {
public void run() {
clhMain.say(msg);
}
});
}
executorService.awaitTermination(20, TimeUnit.SECONDS);
}
}
結果
xxxxxxxxxx
pool-1-thread-1 : 嘗試獲取鎖
pool-1-thread-2 : 嘗試獲取鎖
pool-1-thread-1 : 獲取到鎖
pool-1-thread-1Hello,World?
pool-1-thread-3 : 嘗試獲取鎖
pool-1-thread-4 : 嘗試獲取鎖
pool-1-thread-5 : 嘗試獲取鎖
pool-1-thread-1Hello,World!
pool-1-thread-1Hello,World.
pool-1-thread-1 : 釋放鎖
pool-1-thread-2 : 獲取到鎖
pool-1-thread-2Hello,World?
pool-1-thread-2Hello,World!
pool-1-thread-2Hello,World.
pool-1-thread-2 : 釋放鎖
pool-1-thread-3 : 獲取到鎖
pool-1-thread-3Hello,World?
pool-1-thread-3Hello,World!
pool-1-thread-3Hello,World.
pool-1-thread-3 : 釋放鎖
pool-1-thread-4 : 獲取到鎖
pool-1-thread-4Hello,World?
pool-1-thread-4Hello,World!
pool-1-thread-4Hello,World.
pool-1-thread-4 : 釋放鎖
pool-1-thread-5 : 獲取到鎖
pool-1-thread-5Hello,World?
pool-1-thread-5Hello,World!
pool-1-thread-5Hello,World.
pool-1-thread-5 : 釋放鎖