AQS 的全稱是 Abstract Queued Synchronizer,也就是基于隊(duì)列實(shí)現(xiàn)的抽象同步器
AQS 的主要功能
- 同步狀態(tài)的原子性管理。
- 線程的阻塞和解除阻塞。
- 提供阻塞線程的存儲隊(duì)列。
衍生功能
- 通過中斷實(shí)現(xiàn)的任務(wù)取消俗或,基于線程中斷實(shí)現(xiàn)。
- 可選的超時設(shè)置扶镀,也就是調(diào)用者可以選擇放棄等待蕴侣。
- 定義了Condition接口,用于支持管程形式的await/signal/signalAll操作臭觉,代替了Object類基于JNI提供的wait/notify/notifyAll。
AQS還根據(jù)同步狀態(tài)的不同管理方式區(qū)分為兩種不同的實(shí)現(xiàn):獨(dú)占狀態(tài)的同步器和共享狀態(tài)的同步器辱志。
偽代碼:
// acquire操作如下:
while (synchronization state does not allow acquire) {
enqueue current thread if not already queued;
possibly block current thread;
}
dequeue current thread if it was queued;
//release操作如下:
update synchronization state;
if (state may permit a blocked thread to acquire){
unblock one or more queued threads;
}
為了實(shí)現(xiàn)上述操作蝠筑,需要下面三個基本組件的相互協(xié)作:
- 同步狀態(tài)的原子性管理。
- 等待隊(duì)列的管理揩懒。
- 線程的阻塞與解除阻塞什乙。
CLH (Craig, Landin, and Hagersten) locks等待隊(duì)列
CLH鎖也是一種基于鏈表的可擴(kuò)展、高性能已球、公平的自旋鎖臣镣,申請線程僅僅在本地變量上自旋,它不斷輪詢前驅(qū)的狀態(tài)智亮,假設(shè)發(fā)現(xiàn)前驅(qū)釋放了鎖就結(jié)束自旋忆某。從實(shí)現(xiàn)上看,CLH鎖是一種自旋鎖阔蛉,能確保無饑餓性弃舒,提供先來先服務(wù)的公平性。先看簡單的CLH鎖的一個簡單實(shí)現(xiàn):
public class CLHLock implements Lock {
AtomicReference<QueueNode> tail = new AtomicReference<>(new QueueNode());
ThreadLocal<QueueNode> pred;
ThreadLocal<QueueNode> current;
public CLHLock() {
current = ThreadLocal.withInitial(QueueNode::new);
pred = ThreadLocal.withInitial(() -> null);
}
@Override
public void lock() {
QueueNode node = current.get();
node.locked = true;
QueueNode pred = tail.getAndSet(node);
this.pred.set(pred);
while (pred.locked) {
}
}
@Override
public void unlock() {
QueueNode node = current.get();
node.locked = false;
current.set(this.pred.get());
}
static class QueueNode {
boolean locked;
}
// 忽略其他接口方法的實(shí)現(xiàn)
}
但是這個代碼其實(shí)有點(diǎn)問題状原,不停的死循環(huán)會導(dǎo)致cpu過高聋呢,實(shí)際在實(shí)現(xiàn)的時候會采用釋放鎖的時候通知被阻塞線程的方式。
- todo: 這里還有一些別的優(yōu)化
線程阻塞與喚醒
線程的阻塞和喚醒在JDK1.5之前颠区,一般只能依賴于Object類提供的wait()削锰、notify()和notifyAll()方法,它們都是JNI方法毕莱,由JVM提供實(shí)現(xiàn)器贩,并且它們必須運(yùn)行在獲取監(jiān)視器鎖的代碼塊內(nèi)(synchronized代碼塊中)颅夺,這個局限性先不談性能上的問題,代碼的簡潔性和靈活性是比較低的磨澡。JDK1.5引入了LockSupport類碗啄,底層是基于Unsafe類的park()和unpark()方法,提供了線程阻塞和喚醒的功能稳摄,它的機(jī)制有點(diǎn)像只有一個允許使用資源的信號量java.util.concurrent.Semaphore稚字,也就是一個線程只能通過park()方法阻塞一次,只能調(diào)用unpark()方法解除調(diào)用阻塞一次厦酬,線程就會喚醒(多次調(diào)用unpark()方法也只會喚醒一次)胆描,可以想象是內(nèi)部維護(hù)了一個0-1的計(jì)數(shù)器。
CLH隊(duì)列變體的實(shí)現(xiàn)
獨(dú)占模式與共享模式
Condition的實(shí)現(xiàn)
實(shí)戰(zhàn)篇