Através da análise no artigo anterior, sabemos que existem três maneiras de adquirir bloqueios com modo exclusivo, a saber, obter interrupções sem resposta, para obter interrupções de threads de resposta e obter o tempo limite. Existem também essas três maneiras de adquirir bloqueios no modo compartilhado e são basicamente iguais. Se descobrirmos de uma maneira, podemos entender rapidamente outras maneiras. Embora o código -fonte abstrataqueedSynchronizer tenha mais de mil linhas, ele também é repetido muitas vezes, portanto os leitores não devem ter medo no início. Basta ler pacientemente e lentamente, você naturalmente o entenderá gradualmente. Na minha experiência pessoal, existem vários aspectos mais críticos a serem entendidos ao ler o código -fonte abstrataqueedSynchronizer, a saber, a diferença entre o modo exclusivo e o modo compartilhado, o estado de espera dos nós e o entendimento das filas condicionais. Se você entender esses pontos -chave, a leitura do código -fonte subsequente será muito mais fácil. Obviamente, eles são introduzidos no meu artigo "Java Concurrency Series [1] ---- AbstractQueedSynchronizer Código-fonte Análise", e os leitores podem conferir primeiro. Este artigo analisa o modo de compartilhamento em três maneiras de adquirir bloqueios e uma maneira de liberar bloqueios.
1. Não respondendo à aquisição de interrupções de threads
// adquirindo o bloqueio no modo não interruptível (modo compartilhado) Public Final Void adquiredaryed (int arg) {// 1. Tente adquirir o bloqueio se (TryAcquireshared (arg) <0) {// 2. Se a aquisição falhar, insira esse método doacquirededed (arg); }} // Tente adquirir o bloqueio (modo compartilhado) // Número negativo: indica que a aquisição falhou // valor zero: indica que o nó atual é adquirido com sucesso, mas o nó do sucessor não pode mais obter // número positivo: indica que o nó atual é adquirido com sucesso e o nó sucessor pode obter o sucesso do sucesso, o sucessor e o sucessor e o nó sucessor), o sucessor é o que pode ser necessário para o sucesso (o nó sucessor) e o nó sucessor) pode ser adquirido (o nó sucessor) e o sucessor e o sucessor é o que pode ser necessário (o nó sucessor). UnsupportEdOperationException ();}Chamar o método adquirido é uma maneira de adquirir o bloqueio sem responder às interrupções do encadeamento. Neste método, o TryAcquireshared é chamado pela primeira vez para tentar adquirir o bloqueio. O método TryAcquireshared retorna um estado de aquisição do bloqueio. Aqui, o AQS especifica que, se o status de retorno for negativo, significa que o nó atual não adquirirá o bloqueio. Se 0 significa que o nó atual adquire o bloqueio, mas o nó subsequente não pode mais ser adquirido. Se for positivo, significa que o nó atual adquire o bloqueio, e os nós subsequentes desse bloqueio também podem ser obtidos com sucesso. Quando uma subclasse implementa a lógica da obtenção de bloqueios pelo método TryAcquireshareded, o valor de retorno precisa cumprir esta convenção. Se o valor de retorno de chamar TryAcquireshared for menor que 0, significa que a tentativa de adquirir o bloqueio falhou. Em seguida, ligue para o método DOACQUIRARDARED para adicionar o encadeamento atual à fila de sincronização. Vemos o método doacquiresar -shareed.
// get (modo compartilhado) na fila de sincronização private vazio doacquirareded (int arg) {// Adicione à fila de sincronização nó final nó = addwaiter (node.shared); booleano falhou = true; tente {boolean interromped = false; para (;;) {// obtenha o nó avançado do nó atual nó final p = node.predeCessor (); /Se o nó avançado for um nó da cabeça, tente adquirir o bloqueio novamente se (p == head) {// tente adquirir o bloqueio novamente e retornar o status de aquisição // r <0, indicando que a aquisição falhou // r = 0, indicando que o nó atual é adquirido com sucesso, mas o nó subsequente não pode ser adquirido mais // adquirido int r = TryAcquireshared (arg); if (r> = 0) {// Para esse fim, indica que o nó atual adquiriu com êxito o bloqueio. Neste momento, propagará as informações de status de bloqueio para o nó subsequente setheadandpropagate (nó, r); p.next = null; // Se uma solicitação de interrupção for recebida durante o bloqueio do thread, responda à solicitação nesta etapa se (interrompida) {selfInterrupt (); } falhou = false; retornar; }} // Sempre que a aquisição de bloqueio falha, ele determinará se o thread pode ser suspenso. Se for possível, o thread será suspenso no método ParkandCheckInterrupt se (MustParkAfterFailedAcQuire (P, Node) && ParkandCheckInterrupt ()) {interrupted = true; }}} finalmente {if (falhou) {cancelacquire (nó); }}}Digitando o método doaCquirar -shareed primeiro, chame o método Addwaiter para envolver o encadeamento atual em um nó e coloque -o no final da fila de sincronização. Conversamos sobre o processo de adição de nós ao falar sobre o modo exclusivo, então não vou falar sobre isso aqui. Depois que um nó entra na fila de sincronização, se descobrir que o nó na frente é o nó da cabeça, porque a rosca do nó da cabeça adquiriu a trava e entrou na sala, então é sua vez adquirir a fechadura. Portanto, o nó atual não se pendurará primeiro, mas tentará adquirir o bloqueio novamente. Se a pessoa na frente apenas liberar a fechadura e as folhas, o nó atual poderá obter com sucesso a fechadura. Se a pessoa na frente não tiver liberado o bloqueio, ele chamará o método MustParkafterFailedAcquire. Neste método, o estado do nó da cabeça será alterado para o sinal. Somente garantindo que o estado do nó anterior seja o sinal, o nó atual pode se pendurar com confiança. Todos os tópicos serão suspensos no método ParkandCheckInterrupt. Se o nó atual adquirir com êxito o bloqueio, o método setheadandpropagate será chamado para se estabelecer como o nó da cabeça e acordar o nó que também é o modo compartilhado para trás. Vamos dar uma olhada na operação específica do método setheadandpropagate.
// Defina o nó da cabeça e propaga o estado do bloqueio (modo compartilhado) vazio privado setheadandpropagate (nó nó, int propagate) {nó h = head; // Defina o nó dado como o nó da cabeça sethead (nó); // Se a propagação for maior que 0, significa que o bloqueio pode obter se (propagado> 0 || h == null || h.waitstatus <0) {// obtenha o nó sucessor do nó dado s = node.next; // se o nó sucessor do nó especificado estiver vazio, ou seu estado for um estado compartilhado se (s == null || s.isshared ()) {// acorde o nó sucessor DORELEASHARED (); }}} // Operação de bloqueio de liberação (modo compartilhado) private void doreleashared () {for (;;) {// Obtenha o nó da cabeça do nó síncrono da fila h = head; if (h! = null && h! = cauda) {// obtenha o estado de espera do nó da cabeça int ws = h.waitStatus; // Se o status do nó da cabeça estiver sinal, significa que alguém está filmando para trás se (ws == node.signal) {// Obtenha o estado de espera do nó da cabeça para 0 se (! ComparandSetwaitStatus (h, node.signal, 0)) {continue; } // acorde o nó sucessor UMSTARKSUCESSOR (H); // Se o status do nó da cabeça for 0, significa que ninguém está na fila posterior, basta modificar o estado da cabeça para propagar} else if (ws == 0 &&! ComparaDSetwaitStatus (h, 0, node.propagate)) {continuação; }} // apenas garantindo que o nó da cabeça não tenha sido modificado durante o período, você pode sair do loop se (h == head) {break; }}}Chamar o método setheadandpropagate primeiro se define como o nó da cabeça e, em seguida, decide se deve acordar o nó sucessor com base no valor de retorno do método TryAcquired -shareed aprovado. Como mencionado anteriormente, quando o valor de retorno é maior que 0, significa que o nó atual adquiriu com êxito o bloqueio e o nó subsequente também pode adquirir com sucesso o bloqueio. Neste momento, o nó atual precisa acordar o nó que também está no modo compartilhado. Observe que cada vez que você acorda, é apenas para acordar o próximo nó. Se o último nó não estiver no modo compartilhado, o nó atual entrará diretamente na sala e não acordará o nó adicional. A operação de acordar os nós sucessores no modo compartilhado é executado no método DORELEASHAREDARD. As operações de despertar do modo compartilhado e do modo exclusivo são basicamente as mesmas. Ambos encontram a marca no seu assento (em espera de espera). Se a marca estiver sinal, significa que alguém precisa ajudar a acordá -la mais tarde. Se a marca estiver 0, significa que ninguém está na fila na fila neste momento. No modo exclusivo, se você achar que ninguém está na fila, deixará a fila diretamente. No modo compartilhado, se você achar que ninguém está na fila atrás da fila, o nó atual ainda deixará uma pequena nota antes de sair (defina o status de espera para propagar) para dizer às pessoas posteriores o estado disponível desse bloqueio. Então, quando a pessoa que vem mais tarde pode julgar se deve adquirir diretamente o bloqueio com base nesse estado.
2. Resposta à aquisição de interrupções de threads
// adquirindo o bloqueio no modo interrompível (modo compartilhado) Public Final Void AquireSharededInterruptível (int arg) lança interruptedException {// Primeiro, determine se o thread é interrompido, se for o caso, jogue uma exceção se (thread.interrupted ()) {lança new interruptexception (); } // 1. Tente adquirir o bloqueio se (TryAcquireshared (arg) <0) {// 2. Se a aquisição falhar, digite esse método doacquiredededinterruptível (arg); }} // adquirindo no modo interrompível (modo compartilhado) private vazio doacquirededaryededinterruptível (int arg) lança interruptedException {// insira o nó atual na cauda da fila de sincronização nó final nó = addwaiter (node.shared); booleano falhou = true; tente {for (;;) {// obtenha o nó anterior nó final p = node.predeCessor (); if (p == Head) {int r = TryAcquireshared (arg); if (r> = 0) {setheadandpropagate (nó, r); p.next = null; falhou = false; retornar; }} if (deveparkafterFailedAcQuire (p, node) && parkandcheckinterrupt ()) {// Se o thread receber uma solicitação de interrupção durante o processo de bloqueio, ele lançará imediatamente uma exceção aqui, lançará a nova interrupção interrompida (); }}} finalmente {if (falhou) {cancelacquire (nó); }}}A maneira de adquirir um bloqueio em resposta às interrupções do encadeamento e a maneira de adquirir uma trava em resposta às interrupções do encadeamento é basicamente a mesma no processo. A única diferença é onde responder às solicitações de interrupção do encadeamento. Quando a interrupção do thread não está respondendo à interrupção do thread para adquirir a trava, o thread é despertado do método ParkandCheckInterrupt. Após o despertar, ele retorna imediatamente se a solicitação de interrupção foi recebida. Mesmo que a solicitação de interrupção seja recebida, ela continuará girando até que tenha sido adquirida até que responda à solicitação de interrupção e pendure. O thread responderá imediatamente à solicitação de interrupção após o tópico ser despertado. Se a interrupção do thread for recebida durante o processo de bloqueio, uma interrupção será lançada imediatamente.
3. Defina o tempo limite para obter
// adquirindo o bloqueio com um tempo limitado (modo compartilhado) public final boolean TryAcquiresharedNanos (int arg, long nanostimeout) lança interruptedException {if (thread.interrupted ()) {tiro new interruptedException (); } // 1. Chamando TryAcquireshared para tentar adquirir o bloqueio // 2. Se a aquisição falhar, ligue para doacquiresharednanos devolver o TryAcquireshared (arg)> = 0 || doacquiresharednanos (arg, nanostimeout);} // adquirindo o bloqueio com um tempo limitado (modo compartilhado) private boolean doacquiresharednanos (int arg, nanostimeout longo) lança interruptedException {long lasttime = system.nanotime (); nó final nó = addwaiter (node.shared); booleano falhou = true; tente {for (;;) {// obtenha o nó anterior do nó atual do nó p = node.predeCessor (); if (p == Head) {int r = TryAcquireshared (arg); if (r> = 0) {setheadandpropagate (nó, r); p.next = null; falhou = false; retornar true; }} // Se o tempo limite for usado, a aquisição será encerrada e as informações de falha serão retornadas se (nanostimeout <= 0) {return false; } // 1. Verifique se o requisito de suspensão do encadeamento é atendido (garantido que o status do nó direto é sinal) // 2. Verifique se o tempo de tempo limite é maior que o tempo de rotação se (deveparafterFailedAcquire (p, nó) && nanostimeout> spinfortimeouthreshold) {// Se as duas condições acima forem atendidas, o fio atual será suspenso por um período de tempo Locksupport.Parknanos (este, Nanostimeout); } long agora = System.nanotime (); // Tempo limite sempre subtraia o tempo da aquisição de bloqueio Nanostimeout - = agora - Last Time; última hora = agora; // Se uma solicitação de interrupção for recebida durante o bloqueio, uma exceção será imediatamente lançada se (thread.interrupted ()) {lança new interruptEdException (); }}} finalmente {if (falhou) {cancelacquire (nó); }}}Se você entender os dois métodos de aquisição acima, será muito fácil definir o método de aquisição do tempo limite. O processo básico é o mesmo, principalmente para entender o mecanismo de tempo limite. Se o bloqueio for adquirido pela primeira vez, o método doacquiresharednanos será chamado e o tempo de tempo limite será passado. Depois de entrar no método, o bloqueio será adquirido novamente de acordo com a situação. Se o bloqueio falhar novamente, o encadeamento deve ser considerado suspenso. Neste momento, determinaremos se o tempo de tempo limite é maior que o tempo de rotação. Nesse caso, o tópico será suspenso por um período de tempo. Caso contrário, continuaremos tentando obtê -lo. Depois de cada vez que adquirimos a fechadura, subtrairemos o tempo da fechadura para adquiri -la. Vamos fazer um loop assim até que o tempo limite seja esgotado. Se o bloqueio não tiver sido adquirido, a aquisição será encerrada e o sinalizador de falhas de aquisição será retornado. O thread responde à interrupção do thread ao longo do período.
4. Operações de despedimento de nós no modo compartilhado
// operação da liberação de bloqueio (modo compartilhado) public final boolean releaseshed (int arg) {//1.try para liberar o bloqueio if (tryReleashared (arg)) {// 2. Se a liberação for bem -sucedida, acorde outros threads doreleashared (); retornar true; } return false;} // Tente liberar o bloqueio (modo compartilhado) BOOLEAN TRYRELEASSAREDARED (INT ARG) {THROÇÃO NOVA UNSUPORTEDOPEREDOPERATIONEXCECTION ();} // operação de liberação de bloqueio (modo compartilhado) Void privado DORELEASHAREDARE () {para (;) {// obtê -le); if (h! = null && h! = cauda) {// obtenha o estado de espera do nó da cabeça int ws = h.waitStatus; // Se o status do nó da cabeça estiver sinal, significa que alguém estará na fila mais tarde se (ws == node.signal) {// primeiro atualize o estado de espera do nó da cabeça para 0 se (! ComparandSetwaitStatus (h, node.signal, 0)) {continue; } // Acorde o nó subsequente UMSKSUCESSOR (H); // Se o status do nó da cabeça for 0, significa que ninguém está na fila mais tarde, apenas altera o estado da cabeça para propagar} else if (ws == 0 &&! ComparaDSetWaitStatus (h, 0, node.propagate)) {continuação; }} // O loop só pode ser quebrado se (h == head) {break; }}}Depois que o thread termina o trabalho na sala, ele chamará o método RELEASSARED para liberar o bloqueio. Primeiro, ele chamará o método TryReleashareded para tentar liberar o bloqueio. A lógica de julgamento desse método é implementada pela subclasse. Se a liberação for bem -sucedida, ligue para o método DORELEASHARED para acordar o nó sucessor. Depois de sair da sala, ele encontrará o assento original (nó da cabeça) e veja se alguém deixou pequenas notas no assento (sinal de status). Nesse caso, acorde o nó sucessor. Se não houver (status 0) significa que ninguém está na fila, então a última coisa que ele precisa fazer antes de sair é sair, que é deixar uma pequena nota em seu assento (o status está definido para se propagar) para contar às pessoas atrás da fechadura para adquirir o estado. A única diferença entre todo o processo de liberação de bloqueio e o modo exclusivo é operar nesta última etapa.
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.