Анализ исходного кода CountDownLatch - Countdown ()
В предыдущей статье говорилось о принципе wait () в Countdownlatch с уровня исходного кода. В этой статье рассказывается о обратном отсчете ().
public void countdown () {// countdownlatch sync.ReleaseShared (1);} ↓ открытый окончательный логический релиз (int arg) {// aqs if (tryReleaseShared (arg)) {doreLeaseShared (); вернуть истину; } вернуть false;} ↓ защищенные логические сигнал при переходе на ноль для (;;) {int c = getState (); if (c == 0) вернуть false; int nextc = c-1; if (CompareAndSetState (c, Nextc)) return nextc == 0; }}Через конструктор countdownlatch end = new countdownlatch (2); состояние установлено на 2, так что c == 2, nextc = 2-1,
Затем установите состояние на 1 через следующую операцию CAS.
Защищенные окончательные логические данные CompareandSetState (int ожидаете, int update) {// См. Ниже для установки внутренней системы для поддержки этого возврата untafe.compareandswapint (это, actaffest, ожидайте, обновление); }В настоящее время Nextc не 0 и возвращает False. Подождите, пока метод обратного отсчета () не будет вызовом дважды, состояние == 0, nextc == 0 и возвращает true в это время.
Введите метод DoreLeaseShared ().
DoreLeaseShared (); ↓ Private void doreLeaseShared () { / * * Убедитесь, что выпуск распространяется, даже если есть другие * приобретает /выпуска. Это происходит в обычном * способе попытки разобраться в головке, если он нуждается в * сигнале. Но если это не так, статус установлен для распространения, чтобы * убедиться, что после выпуска продолжается распространение. * Кроме того, мы должны зацикливаться на случай, если добавляется новый узел *, пока мы делаем это. Кроме того, в отличие от других видов использования * unparksccorsor, нам нужно знать, если CAS сбрасывает статус * не удастся, если это так. */ for (;;) {узел H = Head; if (h! = null && h! = tail) {int ws = h.waitstatus; if (ws == node.signal) {if (! CompareandSetWaitStatus (h, node.Signal, 0)) продолжить; // Цикл, чтобы перепроверить случаи Unparksuccessor (h); } else if (ws == 0 &&! CompareandSetwaitStatus (h, 0, node.propagate))) продолжить; // цикл на неудачных CAS} if (h == head) // Цикл, если голова изменил перерыв; }}Вспоминая модель очереди ожидания в это время.
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------.
В настоящее время голова не нулевая или хвост. witfystatus == node.signal, так что введите суждение, если (! CompareandSetWaitStatus (h, node.Signal, 0)).
if (! CompareandSetWaitStatus (h, node.Signal, 0)) ↓ /*** Поле Waithstatus CAS узла. */Private Static Final Boolean CompareandSetWaitStatus (узловой узел, int ожидайте, int update) {return unfee.compareandswapint (узел, Waithstatusoffset, ожидайте, обновление);}Эта операция CAS устанавливает состояние до 0, что означает, что Witfikstatus в голове в настоящее время составляет 0. Модель очереди выглядит следующим образом
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------.
Этот метод возвращает истинно. Введите UnparkSuccessor (H);
unparksuccessor (h); ↓ Private void unparksuccessor (узлом узла) { / * * Если статус отрицательный (т.е. возможный нуждающийся сигнал) попробуйте * очистить в ожидании передачи сигналов. Это нормально, если это * не удалось или если статус изменяется в потоке ожидания. */ int ws = node.waitstatus; if (ws <0) CompareandSetWaitStatus (Node, WS, 0); / * * Поток до unpark проводится в преемнике, который обычно * только следующий узел. Но если отменены или, по-видимому, нулевые, * перейдите в обратно из хвоста, чтобы найти фактического * не канцелимеровного преемника. */ 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);}S - узел преемника головы, то есть узел с текущим потоком. S! = NULL, и S.WaitStatus == 0, так что введите locksupport.unpark (s.thread);
public static void unpark (потока потока) {if (thread! = null) небезопасно. Unpark (Thread); }То есть поток, которая разблокирует заблокирован. Рефери было разрешено взорвать свисток!
Принцип обратного отсчета () очень ясен.
Каждый раз, когда метод обратного отсчета () выполняется, состояние уменьшается на 1. До состояния == 0, потоки, заблокированные в очереди, начнут высвобождаться, а потоки в последующих узлах высвобождаются в соответствии с состоянием Waithstatus в узле предшественника.
Хорошо, вернитесь к вопросу о предыдущей статье, когда разгадает следующий цикл (цикл в методе ожидания)
for (;;) {окончательный узел p = node.predecessor (); if (p == head) {int r = tryAcquireshared (arg); if (r> = 0) {setheadandPropagate (node, r); p.next = null; // Помогите GC FAILL = FALSE; возвращаться; }} if (supparkafterfailedacquire (p, node) && parkandcheckintrupt ()). Выбросить new urrepteDexception ();}В настоящее время, состояние == 0, так что введите метод SetheadandPropagate.
SetheadandPropagate (Node, R); ↓ private void setheadandpropagate (узлы узел, int распространять) {узел H = Head; // Записать старую голову для проверки ниже Sethead (Node); / * * Попробуйте сигнализировать о следующем узле в очереди, если: * распространение было обозначено Caller, * или был записан (как H.WaitStatus либо до *, либо после Sethead) по предыдущей операции * (Примечание: это использует проверку виллинга, потому что * распространение статуса может переходить на сигнал. ненужные пробуждения, но только тогда, когда есть несколько * гонок, приобретает/выпускает, поэтому наиболее нуждаются в сигналах сейчас или скоро * в любом случае. */ if (propagate> 0 || h == null || h.waitstatus <0 || (h = head) == null || h.waitstatus <0) {node s = node.next; if (s == null || s.isshared ()) doreleaseShared (); }} ↓ private void sethead (узловой узел) {head = node; node.thread = null; node.prev = null;}Этот метод меняет узел преемника головы в голову. После этого метода следующий узел узла устанавливается на NULL, и модель становится следующим рисунком
предыдущий +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
То есть хвост узла и другие вещи установлены на NULL, ожидая переработки GC. В это время вернитесь, выпрыгните из петли, и очередь очищена.
Вот демонстрация всего процесса
SetheadandPropagate (Node, R); +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------. Thread = NULL | <---- Узел (хвост) | CurrentThread | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Узел (хвост) | CurrentThread | + -------------------------------+ ↓ +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Ядро Countdownlatch - это очередь блокировки потоков, которая представляет собой очередь, построенная из связанного списка, который содержит потоки и WitfitStatus, где Waitchstatus описывает состояние потока узла преемника.
Государство - очень важный флаг. При построении он устанавливается на соответствующее значение n. Если n! = 0, очередь блокировки будет блокироваться все время, если нить не будет прервана.
Каждый раз, когда используется метод обратного отсчета (), используется State-1, и метод wait () используется для добавления потока, вызывающего метод в очередь блокировки до состояния == 0, и поток не может быть опубликован.
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.