Análise de código -fonte de Countdownlatch - Aguarda (), o conteúdo específico é o seguinte
O artigo anterior falou sobre como usar o Countdownlatch. Este artigo falará sobre o princípio do Wait () do nível do código -fonte.
Já sabemos que aguarda pode manter o encadeamento atual em um estado de bloqueio até que a contagem de trava seja zero (ou interrupção do thread).
Abaixo está o seu código -fonte.
end.await (); ↓ public void aguart () lança interruptedException {sync.acquiresharededinterruptível (1);}Sync é a classe interna de Countdownlatch. Aqui está sua definição.
A classe final estática privada Sync estende abstratoqueedsynchronizer {...}Ele herda o abstratoqueedSynchronizer. AbstractQueUedSynchronizer Esta classe pertence a uma classe muito importante nos threads java.
Ele fornece uma estrutura para implementar bloqueios de bloqueio e sincronizadores relacionados (como sinais, eventos etc.) que dependem de filas de espera do FIFO.
Continue indo e pule para a classe AbstractQuedSynchronizer.
Sync.AcquiredarededInterruptível (1); ↓ Public Final Void AquirearededInterruptível (int arg) // AbstractQueUedSynchronizer lança interruptEdException {if (thread.interrupted ()) lança new interruptedException (); if (TryAcquireshared (arg) <0) doacquiraredaredInterruptível (arg);}Existem dois julgamentos aqui. Primeiro, determine se o tópico é interrompido e depois faça o próximo julgamento. Aqui, olhamos principalmente para o segundo julgamento.
protegido int TryAcquireshared (int adquire) {return (getState () == 0)? 1: -1;}Deve -se notar que o método TryAcquireshared é implementado em sincronia.
Embora existam implementações no TI no AbstractQueedSynchronizer, a implementação padrão é lançar uma exceção.
TryAcquireshared Este método é usado para consultar se o status do objeto atual pode ter permissão para adquirir o bloqueio.
Podemos ver que, em sincronia, retornamos o valor INT correspondente, determinando se o estado é 0.
Então, o que o estado significa?
/*** O estado de sincronização. */ Estado volátil privado int;
O código acima mostra claramente que o estado representa o status de sincronização.
Deve -se notar que o estado usa a palavra -chave volátil para modificá -la.
A palavra -chave volátil pode garantir que a modificação do estado seja atualizada para a memória principal imediatamente. Quando outros threads precisam ler, o novo valor será lido na memória.
Ou seja, a visibilidade do estado é garantida. São os dados mais recentes.
Qual é o estado que vem aqui?
Aqui precisamos dar uma olhada no construtor do Countdownlatch.
Countdownlatch end = new Countdownlatch (2); ↓ Public CountdownLatch (int conting) {if (contagem <0) lançar novo ilegalArgumentException ("contagem <0"); this.sync = new Sync (contagem);} ageAcontece que os números no construtor são usados para definir o estado.
Então temos estado == 2 aqui. TryAcquireshared Returns -1. Digite abaixo
doacquirarharededinginterruptível (arg); ↓ Private Void DoacquiraryDaredInterruptível (int arg) lança interruptEdException {Nó final nó = addwaiter (node.shared); booleano falhou = true; tente {for (;;) {final nó p = node.predeCessor (); if (p == Head) {int r = TryAcquireshared (arg); if (r> = 0) {setheadandpropagate (nó, r); p.next = null; // ajuda gc falhou = false; retornar; }} if (deveparkafterFailedAcQuire (p, node) && parkandcheckinterrupt ()) lança new interruptedException (); }} finalmente {if (falhou) cancelacquire (nó); }}OK, esse código é um pouco longo e várias funções são chamadas nele. Vejamos um por um.
Um novo nó de classe aparece na primeira linha.
O nó é uma classe interna na classe AQS (AbstractQueedSynchronizer), que define uma estrutura de cadeia. Como mostrado abaixo.
+------+prev+-----++-----+cabeça | | <---- | | <---- | | | Tail +----- + +----- + +----- +
Lembre -se dessa estrutura.
Há também um método na primeira linha de código addwaiter (node.shared).
addwaiter (node.shared) //node.shared significa que o nó está no modo compartilhado esforço AddWaiter de nós privados (modo nó) {nó node = new Node (Thread.currentThread (), modo); // tente o caminho rápido do ENQ; backup para enq completo no nó de falha pred = cauda; // cauda de nó volátil transitório privado; if (pred! = null) {node.prev = pred; if (comparaAndStetTail (pred, nó)) {pred.Next = node; Nó de retorno; }} enq (nó); Nó devolver;}Primeiro, um nó é construído e o encadeamento atual é armazenado. O modo é um modo compartilhado.
Tail significa que a extremidade da fila da fila de espera é nula neste momento. Então pred == null entra ENQ (nó);
enq (nó) ↓ nó privado ENQ (nó final) {for (;;) {nó t = cauda; if (t == null) {// deve inicializar se (comparaandSeThead (new node ()))) cauda = head; } else {node.prev = t; if (comparaAndStettail (t, node)) {t.Next = node; retornar t; }}}}A mesma cauda é NULL, entre compareeAndSeThead.
ComparaDeSthead (new Node ()) ∞/*** CASE CASE CAS. Usado apenas por enq. */Private Final Boolean ComparaDeSthead (atualização do nó) {return unsafe.compareandswapobject (this, desvio de traseiro, nulo, atualização);}Esta é uma operação CAS. Se a cabeça for nula, a cabeça da fila de espera será definida como o valor de atualização, que é um novo nó.
cauda = cabeça; Então a cauda não é mais nula neste momento. Digite o próximo ciclo.
Desta vez, primeiro ponto o ponteiro prev do nó na cauda, depois defina o nó na cauda através de uma operação CAS e retorne a cauda da fila, ou seja, o nó.
O modelo da fila de espera muda da seguinte maneira
+ ------+ Anterior +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Ok, quando você chega aqui, o método aguarda retorna, é um tópico igual ao nó do thread atual.
Retorne ao DOACQUIRARHAREDERING INTERRUPPLIAL (INT ARG) e digite o seguinte loop.
para (;;) {Final Node P = Node.PredeCessor (); if (p == Head) {int r = TryAcquireshared (arg); if (r> = 0) {setheadandpropagate (nó, r); p.next = null; // ajuda gc falhou = false; retornar; }} if (deveparkafterFailedAcQuire (p, node) && parkandcheckinterrupt ()) lançar new interruptedException ();}Neste momento, assumindo que o estado ainda seja maior que 0, r <0 no momento, então digite o método MustParkafterFailedAcquire.
deveparkafterfailedacquire (p, nó) ↓ booleano estático privado deveparkafterFailedacquire (nó pred, nó nó) {int ws = pred.waitstatus; if (ws == node.signal) // estático final int sinal = -1; / * * Este nó já definiu o status solicitando uma versão * para sinalizar, para que possa estacionar com segurança. */ return true; if (ws> 0) { / * * O antecessor foi cancelado. Pule sobre predecessores e * Indicado novamente novamente. */ do {node.prev = pred = pred.prev; } while (pred.waitstatus> 0); pred.Next = node; } else { / * * waitstatus deve ser 0 ou propagar. Indique que * precisamos de um sinal, mas ainda não estacionamos. O chamador precisará * tentar novamente para garantir que não possa adquirir antes do estacionamento. */ ComparaDSetWaitStatus (PESS, WS, Node.Signal); } retornar false;} ↓/*** Campo WaitStatus Cas de um nó. */Private estático final boolean ComparaDSetwaitStatus (nó nó, int espera, int update) {return insefa.compareandswapint (nó, waitstatusoffset, espere, atualização);}Você pode ver que deve serparkafterfailedacquire também vai até comparar ossetwaitstatus.
ComparaDSetWaitStatus Defina o WaitStatus do Prev para Node.Signal.
Node.Signal significa que os threads nos nós subsequentes precisam ser desencadeados (semelhantes a serem despertados). Este método retorna false.
Após esse ciclo, o modelo da fila se torna o seguinte estado
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Como deve ser false falsa, não analisaremos mais as seguintes condições. Continue o loop para (;;).
Se o estado ainda for maior que 0, entre novamente no MustParkafterFailedacquire.
Desta vez, porque o WaitStatus na cabeça é node.Signal, deve -separarfterfailedacquire retorna true.
Desta vez, preciso ver o método ParkandCheckInterrupt.
private final boolean parkandcheckinterrupt () {LockSupport.park (this); retornar thread.Interrupted (); }Ok, o tópico não é interrompido, então, retorne falsa. Continue o loop para (;;).
Se o estado é sempre maior que 0 e o encadeamento não for interrompido, está sempre nesse loop. Ou seja, os árbitros mencionados no artigo anterior que sempre relutaram em anunciar o fim do jogo.
Então, em que circunstâncias o loop sairá? Ou seja, em que circunstâncias o estado será menor que 0? Vou explicar o próximo artigo.
Para resumir, o método Wait () é realmente inicializar uma fila, adicione o thread que precisa ser esperado (estado> 0) a uma fila e use WaitStatus para marcar o status do thread do nó sucessor.
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.