В предыдущей статье «Серия параллелизма Java [1] ------ AbstractQueuedSynchronizer Analysis исходного кода», мы представили некоторые основные концепции AbstractQueuedSynchronizer, в основном разговоры о том, как реализована область очереди AQS, каков исключительный режим и общий режим и как понять состояние ожидания. Понимание и освоение этого содержимого является ключом к последующему чтению исходного кода AQS, поэтому рекомендуется, чтобы читатели сначала прочитали мою предыдущую статью, а затем оглянулись на эту статью, чтобы понять ее. В этой статье мы представим, как узлы входят в очередь очередей синхронизации в эксклюзивном режиме и какие операции будут выполнены перед тем, как покинуть очередь синхронизации. AQS предоставляет три способа получить блокировки в эксклюзивном режиме и общем режиме: не реагирующее приобретение прерывания потока, получение прерывания потока ответа и установка тайм-аута. Общие шаги этих трех методов примерно одинаковы, с несколькими разными частями, поэтому, если вы понимаете один метод и посмотрите на реализацию других методов, вы будете похожи. В этой статье я сосредоточусь на методе приобретения не отвечать на прерывания потоков, а два других метода также будут говорить о несоответствиях.
1. Как получить блокировки с нереактивными прерываниями потока?
// не отвечать на приобретение метода прерывания (эксклюзивный режим) public final void accure (int arg) {if (! TryAcquire (arg) && accirequed (addwaiter (node.exclusive), arg)) {selfunterrupt (); }}Хотя приведенный выше код выглядит просто, он выполняет 4 шага, показанные на рисунке ниже по порядку. Ниже мы продемонстрируем и анализируем шаг за шагом.
Шаг 1 :! TryAcquire (arg)
// Попробуйте приобрести блокировку (эксклюзивный режим) защищенный логический
В это время кто -то пришел, и он попытался сначала постучать в дверь. Если бы он обнаружил, что дверь не была заперта (tryAcquire (arg) = true), он бы вошел напрямую. Если вы обнаружите, что дверь заблокирована (tryAcquire (arg) = false), выполните следующий шаг. Этот метод TryAcquire определяет, когда блокировка открыта и когда закрыт замок. Этот метод должен быть перезаписан подклассами и переписать логику суждения внутри.
Шаг 2: AddWaiter (node.exclusive)
// Заверните текущий поток в узл и добавьте его в хвост очереди синхронизации // Получить ссылку на хвостовой узел узла очереди синхронизации Pred = tail; // Если хвостовой узел не пуст, это означает, что очередь синхронизации уже имеет узел, если (Pred! = Null) {// 1. Укажите на текущий узел хвостового узла. Prev = Pred; // 2. Установите текущий узел в хвостовой узел if (CompareandStatail (Pred, Node)) {// 3. Укажите преемника старого хвостового узла на новый хвостовой узел Pred.next = node; вернуть узел; }} // В противном случае это означает, что очередь синхронизации не была инициализирована enq (node); return node;} // enqueue nequeue nequeue private node enq (окончательный узел узла) {for (;;) {// Получить ссылку на хвостовой узел узла очереди синхронизации t = tail; // Если хвостовой узел пуст, это означает, что очередь синхронизации не была инициализирована, если (t == null) {// Инициализируют очередь синхронизации, если (сравнение (new node ())) {tail = head; }} else {// 1. Укажите на текущий узел узла хвостового узла. Prev = t; // 2. Установите текущий узел в хвостовой узел if (CompareandStatail (t, Node)) {// 3. Укажите преемника старого хвостового узла на новый хвостовой узел T.Next = Node; возврат t; }}}}Выполнение на этом шаге указывает на то, что в первый раз, когда приобретение блокировки не удалось, поэтому человек получит для себя номерную карту и войдет в зону очереди в очереди. При получении номерной карты он объявит, как он хочет занять комнату (эксклюзивный режим или режим обмена). Обратите внимание, что он не сел и не отдыхал в это время (повесить трубку).
Шаг 3: Acquirequed (addwaiter (node.exclusive), arg)
// Приобретение блокировки непрерывным образом (эксклюзивный режим) Final Boolean Accirequed (Final Node Node, int arg) {boolean fail = true; try {boolean прерван = false; for (;;) {// Получить ссылку на предыдущий узел данного узла окончательного узла p = node.predecessor (); // Если текущий узел является первым узлом очереди синхронизации, попробуйте получить блокировку if (p == Head && tryAcquire (arg)) {// Установить заданный узел в качестве головного узла Sethead (Node); // Чтобы помочь сбору мусора, очистите преемника предыдущего узла Head P.Next = null; // Установить успешное состояние приобретения не удалось = false; // Вернуть прерванное состояние, здесь выполняется весь цикл, возврат выхода прерывается; } // В противном случае это означает, что статус блокировки все еще недоступен. В настоящее время определите, может ли текущий поток быть приостановлен // Если результат является истинным, текущий поток может быть приостановлен, в противном случае цикл будет продолжаться в течение этого периода, поток не будет реагировать на прерывание, если (должен (должен (должен (должен (p, node) && parkandcheckertrupt ()) {treamted = true; }}} Наконец {// Убедитесь, что отмените получение приобретения if (не удалось) {cancelacquire (node); }}} // Судья, может ли он приостановить текущий узел частного статического логического BOOLEAN SupPARKAFTERFAILEDACQUIRE (NODE PRED, NODE NODE) {// Получить состояние ожидания переднего узла int ws = pred.waitstatus; // Если состояние прямого узла является сигналом, это означает, что прямой узел разбудит текущий узел, поэтому текущий узел может безопасно приостановить if (ws == node.signal) {return true; } if (ws> 0) {// Следующая операция заключается в очистке всех отмененных прямых узлов в очереди синхронизации do {node.prev = pred = pred.prev; } while (Pred.WaitStatus> 0); Pred.next = node; } else {// С этой целью это означает, что состояние прямого узла не является сигналом, и оно, вероятно, будет равным 0. Таким образом, прямой узел не будет разбудить текущий узел .//so Текущий узел должен гарантировать, что состояние прямого узла является сигналом, чтобы безопасно сравнивать. } вернуть false;} // Приостановка текущего потока Частный окончательный логический логический return Thread.Erenterten ();}После получения знака номера он немедленно реализует этот метод. Когда узел впервые попадает в зону очереди, есть две ситуации. Одним из них является то, что он обнаруживает, что человек перед ним покинул свое место и вошел в комнату, поэтому он не сядет и не отдохнет, и снова постучат в дверь, чтобы посмотреть, сделан ли ребенок. Если человек внутри оказался законченным, он бросился бы, не позвонив себе. В противном случае он должен был бы подумать о том, чтобы сидеть и отдыхать некоторое время, но он все еще волновался. Что, если никто не напомнил ему после того, как он сел и уснул? Он оставил небольшую ноту на сиденье человека впереди, чтобы человек, выходящий изнутри, мог разбудить его, увидев записку. Другая ситуация заключается в том, что, когда он вошел в зону очереди и обнаружил, что перед ним было несколько человек, он мог некоторое время сесть, но до этого он все равно оставил записку на месте человека впереди (он уже спал в это время), чтобы человек мог разбудить его перед отъездом. Когда все будет сделано, он мирно спит. Обратите внимание, что мы видим, что у всего петли есть только один выход, то есть он может выйти только после того, как поток успешно приобретает блокировку. Перед получением замка он всегда висел в методе Parkandcheckinterrupt () для петли. После того, как поток разбудится, он также продолжает выполнять цикл из этого места.
Шаг 4: SelfEntruption ()
// текущий поток будет прервать себя частной статической void selfunterrupt () {thread.currentThread (). Enterrupt (); }Поскольку вся нить выше была повешена в методе Parkandcheckinterrupt () для цикла для цикла, он не отвечает на какую -либо форму прерывания потоков до его успешного приобретения. Только когда поток успешно приобретает блокировку и выходит из цикла для цикла, он проверит, просит ли кто -то прервать поток в течение этого периода. Если это так, назовите метод selfuntruption (), чтобы повесить себя.
2. Как получить блокировки в ответ на прерывания потоков?
// Приобретение блокировки в прерываемом режиме (эксклюзивный режим) Private void DoAcquire Internable (int arg) бросает прерванные эктриэкцепции {// Обертывание текущего потока в узел и добавление его в очередь синхронизации окончательного узла узла = addWaiter (node.exclize); Boolean не удалось = true; try {for (;;) {// Получение предыдущего узла окончательного узла p = node.predecsor (); // Если p - узло, то текущий поток пытается снова получить блокировку, если (p == head && tryacquire (arg)) {sethead (node); p.next = null; // Помогите GC FAILL = FALSE; // возврат возврата после успешного приобретения блокировки; } // Если условие выполнено, текущий поток будет приостановлен. В настоящее время отвечает прерывание, и исключено, если (supparkafterfailedacquire (p, node) && parkandcheckinterrupt ()) {// Если поток будет пробужден, если будет обнаружен вопрос, что будет вынесено исключение. выбросить новый прерываний (); }}} наконец {if (Faile) {cancelacquire (node); }}}Метод прерывания потока ответа и метод прерывания без реагирования потока примерно одинаковы в процессе получения блокировки. Единственное отличие состоит в том, что после того, как нить просыпается от метода Parkandcheckinterrupt, она проверит, прерывается ли поток. Если это так, это бросит исключение прерывания. Вместо того, чтобы отвечать на блокировку сбора прерывания потоков, оно устанавливает состояние прерывания только после получения запроса прерывания и не сразу же прекратит текущий метод получения блокировки. Он не решит, повесить ли сам по себе состояние прерывания после того, как узел успешно приобретает замок.
3. Как установить время ожидания, чтобы приобрести замок?
// Приобретение блокировки с ограниченным тайм -аутом (эксклюзивным режимом) Частный логический подраздел Doacquirenanos (int arg, long nanostimeout) бросает прерванные // Обертывание текущего потока в узел и добавление его в очередь синхронизации окончательного узла узла = addWaiter (node.exclusive); Boolean не удалось = true; try {for (;;) {// Получение предыдущего узла окончательного узла p = node.predecsor (); // Если предыдущий узел является узлом головки, то текущий поток пытается снова получить блокировку, если (p == Head && tryAcquire (arg)) {// Обновление узла Head Sethead (Node); p.next = null; Неудача = false; вернуть истину; } // Как только время ожидания используется, выйдите непосредственно, если (nanostimeout <= 0) {return false; } // Если время ожидания превышает время вращения, то после суждения о том, что поток может быть приостановлен, поток будет приостановлен в течение определенного периода времени, если (должен (должен (p, node) && nanostimeout> spinfortimeoutThreshold) {// есть текущий поток в течение периода времени, а затем просыпаться сама по себе locksupormemememe. } // Получить текущее время системы долго теперь = System.nanotime (); // Время тайм -аута вычитается из временного интервала заблокированного блокировки Nanostimeout - = сейчас - прошлое; // Обновление в прошлое время снова прошло прошлое = сейчас; // Исключение бросается, когда запрос прерывания получен во время сбора блокировки if (thread.terrupture ()) {Throw new refruptedException (); }}} наконец {if (Faile) {cancelacquire (node); }}}Установка приобретения времени ожидания сначала приобретет замок. После первого раза приобретение не удалось, оно будет основано на ситуации. Если время входящего временного ожидания больше времени вращения, нить будет приостановлен в течение определенного периода времени, в противном случае он будет вращаться. После каждый раз, когда замок будет приобретен, время ожидания будет вычтено с момента, когда нужно приобрести замок. До тех пор, пока тайм -аут не превышает 0, это означает, что время ожидания было использовано. Затем будет прекращена операция приобретения блокировки, а флаг сбоя приобретения будет возвращен. Обратите внимание, что в процессе получения блокировки со временем ожидания вы можете ответить на запросы на прерывание потоков.
4. Как нить выпускает блокировку и оставляет очередь синхронизации?
// Выпуск операции блокировки (эксклюзивный режим) Public Final Boolean Release (int arg) {// Поверните блокировку пароля, чтобы увидеть, может ли он разблокировать if (tryRelease (arg)) {// Получить узлом узла головного узла H = Head; // Если узел голов не является пустым, а состояние ожидания не равно 0, разбудите узел преемника if (h! = Null && h.waitstatus! = 0) {// Пробуждение узла преемника UnparkSuccessor (h); } вернуть true; } вернуть false;} // Разбуждение узла преемника private void unparkSuccessor (узлы узла) {// Получить состояние ожидания заданного узла int ws = node.waitstatus; // Обновление состояния ожидания до 0 if (ws <0) {CompareandSetwaitStatus (Node, WS, 0); } // Получить последующий узел заданного узла узла s = node.next; // Узел преемника пуст или состояние ожидания отменено, если (s == null || s.waitstatus> 0) {s = null; // Завершить первый узел, который не отменен из очереди отслеживания обратного перехода для (узел t = хвост; t! = Null && t! = Node; t = t.prev) {if (t.waitstatus <= 0) {s = t; }}} // Разбудить первый узел после данного узла, который не является состоянием отмены, если (s! = Null) {locksupport.unpark (s.thread); }}После того, как нить удерживает замок в комнате, она будет вести свой собственный бизнес. После того, как работа будет выполнена, она отпустит замок и покинет комнату. Замок пароля может быть разблокирована с помощью метода TryRelease. Мы знаем, что метод TryRelease должен быть перезаписан подклассом. Правила реализации разных подклассов различны, что означает, что пароли, установленные различными подклассами, разные. Например, в повторном порядке, каждый раз, когда человек в комнате вызывает метод TryRelease, состояние будет уменьшено на 1, пока состояние не будет уменьшено до 0, блокировка пароля будет открыта. Подумайте о том, выглядит ли этот процесс, как будто мы постоянно поворачиваем колесо блокировки пароля, и количество колес уменьшается на 1 каждый раз, когда мы его вращаем. Countdownlatch немного похож на этот, за исключением того, что это не только то, что он поворачивает одного человека, но и то, что он превратится в одного человека, концентрируя силу каждого, чтобы открыть замок. После того, как нить покинет комнату, она найдет свое оригинальное сиденье, то есть найдет узел головного узела. Посмотрите, оставил ли кто -нибудь небольшую заметку для этого на сиденье. Если есть, он будет знать, что кто -то спит и должен попросить его помочь разбудить его, а затем разбудит эту ветку. Если нет, это означает, что никто не ждет в очереди синхронизации в настоящее время, и никому не нужно, чтобы он проснулся, поэтому он может уйти с душевным спокойствием. Приведенный выше процесс - это процесс освобождения блокировки в эксклюзивном режиме.
ПРИМЕЧАНИЕ. Все вышеперечисленные анализы основаны на JDK1.7, и будут различия между различными версиями, читатели должны обратить внимание.
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.