Благодаря анализу в предыдущей статье мы знаем, что существует три способа приобрести блокировки с исключительным режимом, а именно, чтобы получить прерывания потока ответа, получить прерывания потока ответа и получить время ожидания. Есть также эти три способа приобрести замки в общем режиме, и они в основном одинаковы. Если мы выясним один путь, мы сможем быстро понять другие способы. Хотя исходный код AbstractQueuedSynchronizer имеет более тысячи строк, он также повторяется много раз, поэтому читатели не должны напугаться в начале. Просто прочитайте это терпеливо и медленно, вы, естественно, постепенно поймете это. По моему личному опыту, есть несколько более важных аспектов, которые можно понять при чтении исходного кода AbstractQueuedSynchronizer, а именно разницы между исключительным режимом и общим режимом, состоянием узлов и пониманием условных очередей. Если вы понимаете эти ключевые моменты, то последующее чтение исходного кода будет намного проще. Конечно, они представлены в моей статье «Серия параллелизма Java [1] ---- AbstractQueuedSynchronizer Анализ исходного кода», и читатели могут сначала проверить его. В этой статье анализируется режим совместного использования в три способа приобретения замков и один из способов выпуска замков.
1. Не отвечать на получение прерывания потоков
// Приобретение блокировки в неинтерротном режиме (общий режим) публичный окончательный void accireshared (int arg) {// 1. Попробуйте приобрести Lock if (tryAcquireShared (arg) <0) {// 2. Если приобретение не удается, введите этот метод Doacquireshared (arg); }} // Попробуйте приобрести блокировку (общий режим) // Отрицательное число: указывает на то, что приобретение не удалось // нулевое значение: указывает на то, что текущий узел успешно получен, но узел -преемник больше не может получить // Положительное число: указывает на то, что текущий узел успешно приобретен, и преемник также может получить успех.Вызов метода приобретения - это способ получить блокировку, не отвечая на прерывания потока. В этом методе сначала призван TryAcquireShared, чтобы попытаться приобрести замок. Метод TryAcquireShared возвращает состояние приобретения блокировки. Здесь AQS указывает, что если статус возврата является отрицательным, это означает, что текущий узел не может получить блокировку. Если 0 означает, что текущий узел приобретает блокировку, но последующий узел больше не может быть приобретен. Если это положительно, это означает, что текущий узел получает блокировку, а последующие узлы этого блокировки также могут быть успешно получены. Когда подкласс реализует логику получения блокировки методом TryAcquireShared, возвращаемое значение должно соответствовать этому соглашению. Если возвращаемое значение вызова TryAcquireShared составляет меньше 0, это означает, что попытка приобрести блокировку не удалась. Затем вызовите метод Doacquireshared, чтобы добавить текущий поток в очередь синхронизации. Мы видим метод Doacquireshared.
// get (общий режим) в очереди синхронизации private void doacquireshared (int arg) {// Добавить в очередь синхронизации конечный узел узла = addWaiter (node.shared); Boolean не удалось = true; try {boolean прерван = false; for (;;) {// Получить прямой узел текущего узла окончательный узел p = node.predecsor (); // Если прямой узел является головным узлом, попробуйте снова приобрести блокировку, если (p == head) {// Попробуйте снова приобрести блокировку и вернуть состояние приобретения // r <0, что указывает на то, что приобретение не удалось // r = 0, что указывает на то, что текущий узел успешно приобретен, но следующий узел больше не может быть успешным, и не может быть успешным, и не может быть успешным, и не может быть успешным, и не может быть успешным, и не может быть успешным, и не может быть успешным, и не может быть успешным, и не может быть успешным, и не может быть успешным. успешно приобретен int r = tryAcquireshared (arg); if (r> = 0) {// Для этого конца указывает, что текущий узел успешно приобрел блокировку. В настоящее время он будет распространять информацию о состоянии блокировки в последующем узле SetheadandPropagate (Node, R); p.next = null; // Если запрос на прерывание получен во время блокировки потока, ответьте на запрос на этом этапе, если (прервано) {selfInterrupt (); } не удалось = false; возвращаться; }} // Каждый раз, когда сбоя сбоя блокировки будет определять, может ли поток быть приостановлен. Если это возможно, поток будет приостановлен в методе Parkandcheckinterrupt If (должен (должен (должен (должен (p, p, node) && parkandcheckintrupt ()) {прерван = true; }}} наконец {if (Faile) {cancelacquire (node); }}}Войдя в метод Doacquireshared сначала, вызовите метод AddWaiter, чтобы обернуть текущий поток в узел и положить его в конце очереди синхронизации. Мы говорили о процессе добавления узлов при разговоре об эксклюзивном режиме, поэтому я не буду говорить об этом здесь. После того, как узел попадает в очередь синхронизации, если он обнаруживает, что узел перед ним является узлом головки, потому что резьба узла головного узела приобрела блокировку и вошел в комнату, тогда его очередь приобрести блокировку. Поэтому текущий узел не будет сначала повесить себя, но попытается снова приобрести замок. Если человек впереди просто выпускает замок и уходит, текущий узел может успешно получить блокировку. Если человек впереди не выпустил замок, он позвонит в метод ParkafterfailedAcquire. В этом методе состояние головного узла будет изменено на сигнал. Только гарантируя, что состояние предыдущего узла было сигналом, текущий узел может с уверенностью повесить себя. Все нити будут приостановлены в методе Parkandcheckinterrup. Если текущий узел успешно приобретет блокировку, то метод SetheadandPropagate будет вызван, чтобы установить себя в качестве головного узла и разбудить узел, который также является общим режимом. Давайте посмотрим на конкретную работу метода SetheadandPropagate.
// Установите узел головки и распространите состояние блокировки (общий режим) private void setheadandpropagate (узловой узел, int propagate) {node h = head; // Установить заданный узел в качестве узла головы Sethead (Node); // Если распространение больше 0, это означает, что блокировка может получить if (распространять> 0 || h == null || h.waitstatus <0) {// Получить узел преемника заданного узла узла s = node.next; // Если узел преемника данного узла пуст, или его состояние является общим состоянием, если (s == null || s.isshared ()) {// Разбуждение узла преемника doreleaseShared (); }}} // Операция блокировки выпуска (общий режим) private void doreLeaseShared () {for (;;) {// Получить узлом головного узла синхронного узла очереди h = head; if (h! = null && h! = tail) {// Получить состояние ожидания головного узла int ws = h.waitstatus; // Если статус узла головного сигнала является сигналом, это означает, что кто -то находится в очереди за if (ws == node.signal) {// Получить состояние ожидания головного узла до 0 if (! CompareandSetWaitStatus (h, node.signal, 0)) {продолжить; } // Пробуждение узла преемника UnparkSuccorsor (h); // Если статус узла головы равен 0, это означает, что никто не стоят позже, просто измените состояние головы, чтобы распространять} else if (ws == 0 &&! CompareandSetwaitStatus (h, 0, node.propagate)) {продолжение; }} // только гарантируя, что узел головки не был изменен в течение периода, вы можете вырваться из цикла, если (h == head) {break; }}}Вызов метода SetheadandPropagate сначала устанавливается в качестве головного узла, а затем решает, разбудить узел преемника на основе возвращаемого значения прошедшего метода TryAcquireshared. Как упоминалось ранее, когда возвратное значение превышает 0, это означает, что текущий узел успешно приобрел блокировку, а последующий узел также может успешно получить блокировку. В настоящее время текущий узел должен разбудить узел, который также находится в общем режиме. Обратите внимание, что каждый раз, когда вы просыпаетесь, это только для того, чтобы разбудить следующий узел. Если последний узел не находится в общем режиме, текущий узел будет напрямую входить в комнату и не разбудит дальнейший узел. Работа за пробуждение узлов преемников в общем режиме выполняется в методе DoreLeaseSeared. Операции пробуждения общего режима и эксклюзивного режима в основном одинаковы. Оба найдите бренд на своем месте (состояние ожидания). Если бренд является сигналом, это означает, что кто -то должен помочь разбудить его позже. Если бренд равен 0, это означает, что в это время никто не стоит стоить в очереди. В эксклюзивном режиме, если вы обнаружите, что никто не находится в очереди, вы оставите очередь напрямую. В общем режиме, если вы обнаружите, что никто не стоит стоить за очередью, текущий узел по -прежнему оставит небольшую ноту перед отъездом (установите статус ожидания для распространения), чтобы сообщить более поздним людям доступное состояние этого блокировки. Затем, когда человек, который придет позже, может судить, будет ли напрямую приобрести замок на основе этого штата.
2. Ответ на получение прерывания потока
// Приобретение блокировки в прерываемом режиме (общий режим) публичный окончательный void acpireSharedEruptable (int arg) бросает прерывание {// сначала определить, прерывается ли поток, если это так, добавьте исключение, если (Thread.Eruprupted ()) {Throw new urruptEdException (); } // 1. Попробуйте приобрести Lock if (tryAcquireShared (arg) <0) {// 2. Если приобретение не удастся, введите этот метод DoacquiresharedEntruptable (arg); }} // Приобретение в прерываемом режиме (общий режим) Private void DoAcquiResharedEntertible (int arg) бросает прерывание {// вставить текущий узел в хвост синхронизации очереди конечного узла = addWaiter (node.shared); Boolean не удалось = true; try {for (;;) {// Получить предыдущий узел окончательного узла p = node.predecsor (); if (p == head) {int r = tryAcquireshared (arg); if (r> = 0) {setheadandPropagate (node, r); p.next = null; Неудача = false; возвращаться; }} if (supparkafterfailedacquire (p, node) && parkandcheckintrupt ()) {// Если поток получает запрос прерывания во время процесса блокировки, он немедленно выбросит исключение здесь, бросьте новый прерывание ErrupteDexception (); }}} наконец {if (Faile) {cancelacquire (node); }}}Способ сбора блокировки в ответ на прерывания потока и способ сбора блокировки в ответ на прерывания потока в основном одинаковы в процессе. Единственная разница в том, где ответить на запросы на прерывание потоков. Когда прерывание потока не отвечает на прерывание потока для получения блокировки, поток пробуждается от метода Parkandcheckinterrup. После пробуждения он немедленно возвращает, был ли получен запрос на прерывание. Даже если запрос на прерывание будет получен, он будет продолжать вращаться, пока не будет приобретен, пока не ответит на запрос прерывания и не будет не висеть. Поток немедленно ответит на запрос прерывания после того, как поток будет пробужден. Если прерывание потока получено во время процесса блокировки, прерывавшее эктриптация будет немедленно брошено.
3. Установите время ожидания, чтобы получить
// Приобретение блокировки с ограниченным тайм -аутом (общий режим) публичный окончательный логический Boolean TryAcquireSharedNanos (int arg, long nanostimeout) бросает прерывания. } // 1. Вызовет TryAcquireShared, чтобы попытаться приобрести блокировку // 2. Если приобретение не удается, позвоните в Doacquiresharedanos return TryAcquireshared (arg)> = 0 || doacquiresharednanos (arg, nanostimeout);} // Приобретение блокировки с ограниченным тайм -аутом (общий режим) Частный логический ролев Doacquiresharednanos (int arg, long nanostimeout) Throws retruptedException {long last Time = System.nanotime (); Окончательный узел узла = addwaiter (node.shared); Boolean не удалось = true; try {for (;;) {// Получить предыдущий узел текущего узла окончательного узла p = node.predecessor (); if (p == head) {int r = tryAcquireshared (arg); if (r> = 0) {setheadandPropagate (node, r); p.next = null; Неудача = false; вернуть истину; }} // Если время ожидания используется, приобретение будет прекращено, а информация об отказе будет возвращена, если (nanostimeout <= 0) {return false; } // 1. Проверьте, выполняется ли требование приостановки потока (гарантированное состояние напряжения узла - это сигнал) // 2. Проверьте, превышает ли время времени время время вращения, если (должен (должен (должен (parkafterfailedacquire (p, node) && nanostimeout> spinfortimeoutthreshold) {// Если вышеупомянутые два условия будут выполнены, текущий поток будет приостановлен в течение периода времени LockSupport.parknanos (this, nanostimeout); } long Now = System.nanotime (); // Время тайм -аута Каждый раз, когда вычитает время приобретения блокировки nanostimeout - = сейчас - в прошлое; прошлое = сейчас; // Если запрос на прерывание получен во время блокировки, исключение будет немедленно брошено, если (Thread.Erenprupted ()) {Through New SurpruptEdException (); }}} наконец {if (Faile) {cancelacquire (node); }}}Если вы понимаете два вышеупомянутых метода сбора, будет очень легко установить метод сбора временного времени. Основной процесс такой же, в основном для понимания механизма тайм -аута. Если замок будет приобретен в первый раз, будет вызван метод DoacquiresharedNanos, и время ожидания будет передано. После входа в метод замок будет приобретен снова в соответствии с ситуацией. Если блокировка снова не удается, поток должен считаться приостановленным. В настоящее время мы определим, больше ли времени времени времени, чем время вращения. Если это так, поток будет приостановлен в течение определенного периода времени. В противном случае мы будем продолжать пытаться получить его. После каждого раз, когда мы приобретаем замок, мы вычнем время замка, чтобы приобрести его. Мы будем такими, пока время ожидания не исчерпано. Если замок не был приобретен, приобретение будет прекращено, а флаг сбоя приобретения будет возвращен. Поток отвечает на прерывание потока в течение всего периода.
4. Операции узлов в общем режиме
// Операция выпуска блокировки (общий режим) Общедоступный окончательный логический резолиз Если релиз успешно, разбудите другие потоки DoreLeaseShared (); вернуть истину; } вернуть false;} // Попробуйте выпустить блокировку (общий режим) защищенный Boolean TryReleaseShared (int arg) {бросить новый UnsupportedOperationException ();} // Операция выпуска блокировки (общий режим) private void doreLeaseShared () {for (;;) {// Получить головную ноду синхронового Queue nod nod nod nod nod hud; if (h! = null && h! = tail) {// Получить состояние ожидания головного узла int ws = h.waitstatus; // Если статус узла головного сигнала является сигналом, это означает, что кто -то находится в очереди позже, если (ws == node.signal) {// сначала обновить состояние ожидания головного узла до 0 if (! CompareandSetwaitStatus (h, node.signal, 0)) {продолжение; } // Пробуждение последующего узла UnparkCcessor (h); // Если статус узла головы равен 0, это означает, что никто не стоят позже, это просто изменяет состояние головы, чтобы распространять} else if (ws == 0 &&! CompareandSetwaitStatus (h, 0, node.propagate)) {продолжение; }} // Цикл может быть сломан только если (h == head) {break; }}}После того, как нить заканчивает работу в комнате, она позвонит в метод выпуска, чтобы выпустить блокировку. Во -первых, он позвонит в метод TryReleaseShared, чтобы попытаться выпустить блокировку. Логика суждения этого метода реализована подклассом. Если релиз успешно, позвоните в DoreLeaseShared метод, чтобы разбудить узел преемника. После выхода из комнаты он найдет оригинальное сиденье (узел головки) и посмотрите, оставил ли кто -нибудь небольшие заметки на сиденье (сигнал состояния). Если это так, разбудите узел преемника. Если нет (статус 0) означает, что в очереди никто не находится в очереди, то последнее, что он должен сделать перед уходом, - это уйти, то есть оставить небольшую ноту на своем месте (статус должен распространять), чтобы сообщить людям за замком, чтобы приобрести штат. Единственная разница между всем процессом выпуска блокировки и эксклюзивным режимом состоит в том, чтобы работать на этом последнем шаге.
ПРИМЕЧАНИЕ. Все вышеперечисленные анализы основаны на JDK1.7, и будут различия между различными версиями, читатели должны обратить внимание.
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.