Анализ исходного кода CountDownLatch - wawait (), конкретный контент заключается в следующем
В предыдущей статье говорилось о том, как использовать CountDownLatch. В этой статье рассказывается о принципе wait () с уровня исходного кода.
Мы уже знаем, что ожидание может сохранить текущую резьбу в состоянии блокировки до тех пор, пока количество защелок не станет нулевым (или прерыванием потока).
Ниже приведен исходный код.
end.await (); ↓ Public void wait () бросает прерванную экспрессию {sync.AcquiresharedEntertaill (1);}Синхронизация - это внутренний класс Countdownlatch. Вот его определение.
Private Static Final Class Sync ExtrableCequeudSynchronizer {...}Это наследует AbstractQueuedSynchronicrizer. AbstractQueuedSynchronizer Этот класс принадлежит очень важному классу в нити Java.
Он предоставляет структуру для реализации блокирующих замков и связанных с ними синхронизаторов (таких как сигналы, события и т. Д.), Которые полагаются на очереди ожидания FIFO.
Продолжайте и прыгайте в класс AbstractQueuedSynchronizer.
Sync.AcquireSharedEntertaill (1); ↓ Public Final void accireSharedEructible (int arg) // AbstractQueuedSynchronizer Throws прерывает ExtruptException {if (Thread.Erenprupted ()) бросить новый прерываний (); if (tryAcquireshared (arg) <0) DoAcquiResharedEntertible (arg);}Здесь есть два суждения. Сначала определите, прерывается ли поток, а затем вынесите следующее суждение. Здесь мы в основном смотрим на второе суждение.
Защищенный int tryAcquireshared (int приобретает) {return (getState () == 0)? 1: -1;}Следует отметить, что метод TryAcquireShared реализован в синхронизации.
Несмотря на то, что в AbstractQueuedSynChronizer есть реализации, реализация по умолчанию заключается в том, чтобы сделать исключение.
TryAcquireShared Этот метод используется для запроса, может ли статус текущего объекта разрешено приобрести блокировку.
Мы можем видеть, что в синхронизации мы возвращаем соответствующее значение int, определив, составляет ли состояние 0.
Так что значит состояние?
/*** Состояние синхронизации. */ частное летучие состояния;
Приведенный выше код ясно показывает, что состояние представляет статус синхронизации.
Следует отметить, что состояние использует нестабильное ключевое слово для его изменения.
Колатичное ключевое слово может гарантировать, что изменение состояния обновляется в основную память немедленно. Когда другие потоки должны прочитать, новое значение будет прочитано в памяти.
То есть видимость государства гарантирована. Это последние данные.
Какое состояние приходит сюда?
Здесь нам нужно взглянуть на конструктор Countdownlatch.
Countdownlatch end = new Countdownlatch (2); ↓ public countdownlatch (int count) {if (count <0) бросить новый allosalargumentException ("count <0"); this.sync = new Sync (count);} ↓ Sync (int count) {setState (count);}Оказывается, что числа в конструкторе используются для установки состояния.
Итак, у нас есть состояние == 2 здесь. TryAcquireshared возвращает -1. Введите ниже
DoacquiresharedEntertaill (arg); ↓ private void doacquiresharedEntertable (int arg) бросает прерывание. Boolean не удалось = true; try {for (;;) {окончательный узел p = node.predecsors (); if (p == head) {int r = tryAcquireshared (arg); if (r> = 0) {setheadandPropagate (node, r); p.next = null; // Помогите GC FAILL = FALSE; возвращаться; }} if (supparkafterfailedAcquire (p, node) && parkandcheckinterrupt ()) бросить новый прервант. }} наконец {if (не удалось) cancelacquire (node); }}Хорошо, этот код немного длинный, и в нем вызываются несколько функций. Давайте посмотрим на это один за другим.
Новый узел класса появляется в первой строке.
Узел - это внутренний класс в классе AQS (AbstractQueuedSynchronicrieser), который определяет структуру цепи. Как показано ниже.
+------+prev+-----++-----+голова | | <---- | | <---- | | | хвост +----- + +----- + +----- +
Помните эту структуру.
Существует также метод в первой строке кода AddWaiter (node.shared).
addWaiter (node.shared) //node.shared означает, что узел находится в общем режиме ↓ Private Node AddWaiter (режим узла) {Node Node = новый узел (thread.currentThread (), mode); // попробуйте быстрый путь ENQ; Резервное копирование на полное ENQ на узел сбоя Pred = Tail; // частный переходной летучий хвост узла; if (pred! = null) {node.prev = pred; if (CompareandStatail (pred, node)) {pred.next = node; вернуть узел; }} enq (node); вернуть узел;}Во -первых, построен узел, и текущий поток хранится. Режим является общим режимом.
Хвост означает, что в данный момент конец очереди ожидания нулевой. Итак, Pred == NULL входит ENQ (NODE);
Enq (Node) ↓ Private Node Enq (окончательный узел узла) {for (;;) {node t = tail; if (t == null) {// должен инициализировать if (comparandsethead (new node ())) hail = head; } else {node.prev = t; if (CompareAndsettail (t, node)) {t.next = node; возврат t; }}}}Тот же хвост нулевой, введите сравнение.
CompareandEndsethead (new Node ()) ↓/*** Поле головы CAS. Используется только по enq. */private final Boolean CompareandEdheate (обновление узла) {return uncafe.compareAndswapoboct (это, Headoffset, Null, Update);}Это операция CAS. Если голова нулевой, голова очереди ожидания будет установлена на значение обновления, которое является новым узлом.
хвост = голова; Тогда хвост больше не является нулевым в это время. Введите следующий цикл.
На этот раз сначала укажите предварительный указатель узла на хвост, затем установите узел на хвост через работу CAS и верните хвост очереди, то есть узел.
Модель очереди ожидания изменяется следующим образом
+ ------+ prov +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------.
Хорошо, когда вы попадете сюда, метод ожидания возвращается, это поток, равный узлу текущего потока.
Вернитесь в DoacquiresharedEntertaill (int arg) и введите следующий цикл.
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, а затем R <0 в настоящее время введите метод shopparkafterfailedacquire.
должен ParkafterfailedAcquire (P, Node) ↓ Private Static Boolean SupparkafterfailedAcquire (Node Pred, Node Node) {int WS = Pred.WaitStatus; if (ws == node.signal) // Статический конечный сигнал int = -1; / * * Этот узел уже установил статус с просьбой о выпуске * для его сигнала, чтобы он мог безопасно парковаться. */ return true; if (ws> 0) { / * * Предшественник был отменен. Пропустите предшественники и * указали повторение. */ do {node.prev = pred = pred.prev; } while (Pred.WaitStatus> 0); Pred.next = node; } else { / * * Waitchstatus должен быть 0 или распространять. Укажите, что нам нужен сигнал, но еще не парковайтесь. Абонент должен будет * повторно, чтобы убедиться, что он не может приобрести перед парковкой. */ CompareAndsetWaitStatus (Pred, WS, Node.Signal); } вернуть false;} ↓/*** CAS VaifleTatus Поле узла. */Private Static Final Boolean CompareandSetWaitStatus (узловой узел, int ожидайте, int update) {return unfee.compareandswapint (узел, Waithstatusoffset, ожидайте, обновление);}Вы можете видеть, что должен ParkafterfailedAcquire также проходит до сравнения.
CompareAndsetWaitStatus Установите Waitchstatus Prev в Node.Signal.
Node.Signal означает, что потоки в последующих узлах должны быть не обращением (аналогично пробуждению). Этот метод возвращает false.
После этого цикла модель очереди становится следующим состоянием
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Поскольку должен вернуть False ParkAfterfailedAcquire, мы больше не будем смотреть на следующие условия. Продолжить петлю в (;;);).
Если государство все еще больше 0, введите снова, чтобы ParkafterfailedAcquire.
На этот раз, потому что Waitchstatus in Head - Node.Signal, должен Parkafterfailedacquire, возвращается правдой.
На этот раз мне нужно увидеть метод Parkandcheckinterrup.
Частный финальный логический балансовый return Thread.Erenprupten (); }
ОК, поток не прерывается, поэтому верните False. Продолжить петлю в (;;);).
Если состояние всегда больше 0, а поток не прерывается, то оно всегда находится в этом цикле. То есть судьи упомянули в предыдущей статье, что они всегда неохотно объявляют о конце игры.
Итак, при каких обстоятельствах выпадет петля? То есть при каких обстоятельствах будет утверждать меньше 0? Я объясню следующую статью.
Подводя итог, метод wait () на самом деле для инициализации очереди, добавьте поток, которого нужно ждать (состояние> 0) в очередь, и использовать Waitchstatus, чтобы отметить состояние потока узла преемника.
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.