Para aprender a programação de concorrência Java, temos que aprender sobre o pacote java.util.Concurrent. Existem muitas classes de ferramentas de simultaneidade que costumamos usar neste pacote, como: Reentrantlock, Countdownlatch, CyclicBarrier, semáforo, etc. A implementação subjacente dessas classes depende da classe abstrataqueedSynchronizer, que mostra a importância dessa classe. Assim, na série Java Concurrency, analisei a classe abstrataqueedSynchronizer. Como essa classe é mais importante e o código é relativamente longo, a fim de analisá -lo o mais detalhadamente possível, decidi usar quatro artigos para dar uma introdução relativamente completa a esta classe. Este artigo é uma introdução resumida para dar aos leitores uma compreensão preliminar dessa categoria. Por uma questão de simplicidade da narração, alguns lugares usarão o AQS para representar essa classe no futuro.
1. Para que serve a classe abstrataqueedSynchronizer?
Acredito que muitos leitores usaram o Reentrantlock, mas eles não conhecem a existência de abstratoqueedsynchronizer. De fato, o Reentrantlock implementa uma sincronização de classe interna, que herda abstrataqueedsynchronizer. Todas as implementações do mecanismo de bloqueio dependem das classes internas sincronizadas. Também se pode dizer que a implementação do ReentrantLock depende da classe abstrataqueedSynchronizer. Da mesma forma, as classes CountdownLatch, CyclicBarrier e Semaphore também usam o mesmo método para implementar seu próprio controle de bloqueios. Pode -se observar que o abstratoqueedsynchronizer é a pedra angular dessas classes. Então, o que exatamente é implementado dentro do AQS para que todas essas classes dependam disso? Pode -se dizer que o AQS fornece infraestrutura para essas classes, ou seja, fornece um bloqueio de senha. Depois que essas aulas têm um bloqueio de senha, elas podem definir a senha do bloqueio de senha sozinha. Além disso, o AQS também fornece uma área de fila e um instrutor de thread. Sabemos que os tópicos são como um bárbaro primitivo. Eles não sabem ser educados. Eles só vão se apressar, então você precisa ensiná -lo passo a passo, diga quando precisar na fila, onde fazer fila, o que fazer antes de fazer fila e o que fazer depois da fila. Todo esse trabalho educacional é concluído pelo AQS para você. Os fios educados a partir dele tornaram -se muito civilizados e educados e não são mais bárbaros primitivos. Então, no futuro, precisamos apenas lidar com esses fios civilizados. Nunca tenha muito contato com os threads originais!
2. Por que o AbstractQueUedSynchronizer fornece um bloqueio de senha?
// O nó da cabeça da fila de sincronização Cabeça de nós volátil privada; // o nó da cauda da fila de sincronização cauda de nó volátil transitória privada; // o estado volátil privado int; // obtém o estado de sincronização protegido final int getState () {Return State;} // Definir Syncronization State Introcetect Signation Setstatate (Int NewState) {State = NewState;} // {return unsafe.compareandswapint (isto, stateoffset, espere, atualização);}O código acima lista todas as variáveis de membros do AQS. Você pode ver que existem apenas três variáveis de membros do AQS, a saber, a referência do nó da cabeça da fila de sincronização, a referência do nó da fila de sincronização e o estado de sincronização. Observe que todas as três variáveis de membros são modificadas com a palavra-chave volátil, o que garante que vários threads modificem-a visível na memória. O núcleo de toda a classe é esse estado de sincronização. Você pode ver que o estado de sincronização é na verdade uma variável do tipo int. Você pode considerar esse estado de sincronização como um bloqueio de senha e também é um bloqueio de senha bloqueado da sala. O valor específico do estado é equivalente à senha que controla a abertura e o fechamento do bloqueio de senha. Obviamente, a senha desse bloqueio é determinada por cada subclasse. Por exemplo, no reentrantlock, o estado é igual a 0 significa que a trava está aberta, o estado maior que 0 significa que a trava está travada e, em semáforo, o estado maior que 0 significa que a trava está aberta e o estado é igual a 0 significa que a trava está travada.
3. Como a área da fila do abstratoqueedSynchronizer é implementada?
Na verdade, existem duas áreas de fila dentro de abstratoqueedsynchronizer, uma é uma fila síncrona e a outra é uma fila condicional. Como pode ser visto na figura acima, há apenas uma fila de sincronização, enquanto pode haver várias filas de condição. Os nós da fila síncrona seguram referências aos nós frontal e traseira, respectivamente, enquanto os nós da fila condicional têm apenas uma referência ao nó sucessor. Na figura, t representa um fio. Cada nó contém um thread. Depois que o thread não adquirir o bloqueio, ele primeiro entra na fila de sincronização para fazer fila. Se você deseja inserir a fila condicional, o thread deve manter a trava. Em seguida, vamos dar uma olhada na estrutura de cada nó na fila.
// Os nós da fila síncrona são o nó da classe final estática {nó final estático compartilhado = new Node (); // O encadeamento atual mantém o bloqueio no modo compartilhado estático node exclusivo = null; // O encadeamento atual mantém o bloqueio no modo exclusivo estático final int cancelado = 1; // O nó atual cancelou o sinal final estático de bloqueio Int = -1; // Os threads do nó sucessor precisam executar a condição estática final int = -2; // O nó atual é fila na fila condicional estática final int propagate = -3; // O nó subsequente pode adquirir diretamente o bloqueio volátil int waitstatus; // denotar o estado de espera do nó atual nó volátil prev; // denote o nó avançado na fila de sincronização nó volátil a seguir; // denote o nó sucessor na fila de sincronização Thread volátil thread; // O fio mantido pelo nó atual refere -se ao Node Nextwaiter; // denotar o nó sucessor na fila condicional // é o estado do nó atual no modo compartilhado final boolean isShared () {return nextwaiter == compartilhado; } // retorna o nó avançado do nó atual predecessor () lança NullPointerException {nó p = prev; if (p == null) {lança new nullPointerException (); } else {return p; }} // construtor 1 node () {} // construtor 2, este construtor é usado por nó padrão (encadeamento do thread, modo nó) {// observe que o modo de retenção é atribuído ao nextwaiter this.Nextwaiter = Mode; this.thread = thread; } // Construtor 3, apenas nó (encadeamento, int waitstatus) é usado na fila de condições {this.waitStatus = waitstatus; this.thread = thread; }}O nó representa um nó na fila de sincronização e na fila condicional. É uma classe interna de abstrataqueedsynchronizer. O Node possui muitos atributos, como modo de retenção, estado de espera, pré-sequência e sucessor em filas síncronas e referências sucessoras em filas condicionais, etc. A fila de sincronização e a fila de condição podem ser consideradas uma área de fila, cada nó é considerado um assento na área da fila e a rosca. Quando os convidados chegarem, eles batem na porta para ver se a fechadura está aberta. Se a fechadura não for aberta, eles irão para a área da fila para coletar uma placa de matrícula, declarará como eles querem segurar a fechadura e, finalmente, fila no final da fila.
4Como entender o modo exclusivo e o modo de compartilhamento?
Como mencionado anteriormente, cada hóspede receberá uma placa de matrícula antes de fazer fila e declarar que deseja possuir a fechadura. A maneira de possuir o bloqueio é dividida em modo exclusivo e modo de compartilhamento. Então, como você entende o modo exclusivo e o modo de compartilhamento? Eu realmente não consigo encontrar nenhuma boa analogia. Você pode pensar em um banheiro público. Pessoas com modo exclusivo são mais dominadoras. Eu não entro. Quando entro, não permito que outras pessoas entrem. Ocponho o banheiro inteiro sozinho. As pessoas no modo de compartilhamento não são tão particulares. Quando eles acham que o banheiro já é utilizável, ele não conta se entrar por si só. Eles também precisam perguntar com entusiasmo as pessoas por trás se elas se importam em usá -lo juntas. Se as pessoas por trás não se importam em usá -lo juntas, não há necessidade de fazer fila. Todos vão ir juntos. Obviamente, se as pessoas por trás delas se mentem, elas precisam permanecer na fila e continuar na fila.
5 Como entender o estado de espera de um nó?
Também vemos que cada nó possui um estado de espera, que é dividido em quatro estados: cancelado, sinalização, condição e propagação. Esse estado de espera pode ser considerado como uma placa pendurada ao lado do assento, identificando o estado de espera da pessoa no assento atual. O status desta marca pode não ser apenas modificado por você, mas outros também podem modificá -la. Por exemplo, quando este tópico já planeja desistir durante a fila, ele definirá a placa no assento para cancelar, para que outros possam limpá -la da fila se o virem. Outra situação é que, quando o fio está prestes a adormecer no assento, tem medo de que ela dorme demais, para que mude a placa na posição frontal para sinalizar, porque todos retornarão aos seus assentos antes de deixar a fila para dar uma olhada. Se perceber que o status do sinal é sinal, ele acordará a próxima pessoa. Somente garantindo que a marca na posição frontal seja sinal, o fio atual dormirá pacificamente. O status da condição indica que a rosca é filada na fila condicional. O status de propagação lembra os threads subsequentes para adquirir o bloqueio diretamente. Esse status é usado apenas no modo compartilhado e será discutido posteriormente ao falar sobre o modo compartilhado separadamente.
6. Quais operações serão executadas quando um nó entrar na fila de sincronização?
// operação de enquadra do nó, retorne ao nó anterior nó privado ENQ (nó final) {for (;;) {// Obtenha a referência ao nó da cauda do nó da fila de sincronização t = cauda; // Se o nó da cauda estiver vazio, significa que a fila de sincronização não foi inicializada se (t == null) {// inicialize a fila de sincronização if (comparaandSthead (new Node ())) {cauda = cabeça; }} else {// 1. Aponte para o nó de cauda atual.prev = t; // 2. Defina o nó atual no nó da cauda se (comparaDaTettail (t, nó)) {// 3. Aponte o sucessor do nó da cauda antiga para o novo nó da cauda t.next = nó; // a única saída do retorno de loop for; }}}}Observe que a operação de enQuação usa um loop morto. Somente quando o nó for adicionado com sucesso à cauda da fila de sincronização, ela será retornada. O resultado é o nó da cauda original da fila de sincronização. A figura a seguir mostra todo o processo de operação.
Os leitores precisam prestar atenção à ordem de adição de nós de cauda, que são divididos em três etapas: apontando para nós de cauda, o CAS altera os nós da cauda e apontando os sucessores do nó antigo da cauda para o nó atual. Em um ambiente simultâneo, essas três etapas podem não ser garantidas para serem concluídas. Portanto, na operação de limpar todos os nós cancelados na fila de sincronização, a fim de encontrar nós em um estado não cancelado, ela não é atravessada de frente para trás, mas de trás para frente. Além disso, quando cada nó entra na fila, seu estado de espera é 0. Somente quando o encadeamento do nó subsequente precisa ser suspenso, o estado de espera do nó anterior será alterado para sinalizar.
Nota: Toda a análise acima é baseada no JDK1.7, e haverá diferenças entre diferentes versões, os leitores precisam prestar atenção.
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.