Структура диаграммы классов AQS
Полное имя AQS — AbstractQueuedSynchronizer, которое представляет собой абстрактную очередь синхронизации. Давайте взглянем на структуру диаграммы классов AQS:
государственная собственность
В AQS поддерживается изменчивое измененное состояние единого общего состояния для достижения синхронизации синхронизатора. В его обновлении используется алгоритм оптимистичной блокировки CAS.
очередь CLH
Синхронная очередь CLH (замки Крейга, Лэндина и Хагерстена) представляет собой двустороннюю очередь FIFO, которая записывает элементы головы и хвоста очереди через узлы головы и хвоста, а тип элементов очереди — Node. AQS полагается на него для завершения управления состоянием синхронизации.Если текущему потоку не удается получить состояние синхронизации, AQS создаст узел (узел) с такой информацией, как состояние ожидания текущего потока, и добавит его в очередь синхронизации CLH. , пока блокируется. Текущий поток, когда состояние синхронизации освобождается, разбудит первый узел (справедливая блокировка), чтобы он снова попытался получить состояние синхронизации.
Узел узел
В очереди синхронизации CLH узел представляет поток, который сохраняет ссылку на поток (поток), состояние (waitStatus), предшествующий узел (предыдущий), последующий узел (следующий) и последующий узел (nextWaiter) очереди условий. следующее:
waitStatus несколько состояний состояния:
зачислен
Запись в очередь CLH означает, что хвост указывает на новый узел, предыдущий узел нового узла указывает на текущий последний узел, а следующий из текущего последнего узла указывает на текущий узел. Метод addWaiter выглядит следующим образом:
//构造Node
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
//CAS设置尾节点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//多次尝试
enq(node);
return node;
}
Если addWaiter не может установить хвостовой узел, вызовите метод enq(Node node), чтобы установить хвостовой узел.Метод enq выглядит следующим образом:
private Node enq(final Node node) {
//死循环
for (;;) {
Node t = tail;
//tail不存在,则设置为首节点
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
исключать из очереди
После того, как поток первого узла освободит состояние синхронизации, он разбудит свой узел-преемник (следующий), и узел-преемник установит себя в качестве первого узла, когда состояние синхронизации будет успешно получено. Вы можете увидеть следующий исходный код:
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
Поток метода получения выглядит следующим образом:
Эксклюзивный и общий режим
AQS поддерживает два режима синхронизации: эксклюзивный и общий.
Эксклюзивный
Только один поток одновременно удерживает состояние синхронизации, например ReentrantLock. Его можно разделить на честный замок и несправедливый замок.
- Справедливая блокировка: В соответствии с порядком очереди потоков в очереди вежливая блокировка в порядке поступления.
- Несправедливые блокировки: когда поток хочет получить блокировку, он захватывает ее напрямую, независимо от порядка очереди.
общий
Несколько потоков могут выполняться одновременно, например, Semaphore/CountDownLatch и т. д. являются общими продуктами.