Countdownlatch é uma classe de ferramentas útil. Usando -o, podemos interceptar um ou mais threads a serem executados depois que uma determinada condição estiver madura. Seu conteúdo interno fornece um contador e o valor inicial do contador deve ser especificado ao construir um bloqueio, e o valor inicial do contador deve ser maior que 0. Além disso, ele também fornece um método de contagem regressiva para operar o valor do contador. O contador será diminuído em 1 sempre que o método de contagem regressiva for chamado. Quando o valor do contador é reduzido para 0, significa que as condições estão maduras e todos os threads bloqueados chamando o método aguardando serão despertados. Este é o mecanismo interno do Countdownlatch. Parece muito simples, nada mais é do que bloquear alguns threads e permitir que eles executem depois de atingir uma certa condição. No entanto, o CountdownLatch possui uma ampla gama de cenários de aplicativos. Enquanto você tiver uma mente grande e usá -la, poderá fazer vários truques. O cenário de aplicação mais comum é permitir que vários threads executem uma tarefa ao mesmo tempo e, em seguida, contem e resumisse os resultados após a execução de todas as tarefas. A figura a seguir demonstra dinamicamente todo o processo de bloqueio de threads.
A figura acima mostra que 5 threads são bloqueados chamando o método aguardando e precisam esperar que o valor do contador diminua para 0 antes de continuar a executar. O valor inicial do contador é especificado ao construir um bloqueio e é subsequentemente reduzido por 1 com cada chamada para o método de contagem regressiva. O código a seguir publica o método de construção de Countdownlatch.
// construtor public CountDownLatch (int conting) {if (count <0) lançar novo ilegalArgumentException ("contagem <0"); this.sync = new Sync (contagem); }O CountdownLatch possui apenas um construtor de parâmetro e um valor maior que 0 deve ser passado como o valor inicial do contador, caso contrário, um erro será relatado. Você pode ver isso no construtor, apenas um novo objeto de sincronização e atribui -lo à sincronização da variável do membro. Como outras classes de ferramentas de sincronização, a implementação do CountdownLatch depende do AQS, que é um aplicativo no modo compartilhado AQS. O CountdownLatch implementa uma sincronização de classe interna e a usa para herdar AQs, para que a maioria dos métodos fornecidos pelo AQS possa ser usada. Vamos dar uma olhada no código de sincronização de classe interna.
// Synchronizer private Static Final Class SYNC Estende abstratoQueUedSynchronizer {// Sync (int conting int) {SetState (contagem); } // Obtenha o estado de sincronização atual int getCount () {return getState (); } //Try to obtain the lock//Return negative number: indicates that the current thread failed to obtain//Return zero value: indicates that the current thread has been acquired successfully, but the subsequent thread can no longer obtain //Return positive number: indicates that the current thread has been acquired successfully, and the subsequent thread can also obtain success protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1: -1; } // Tente liberar o bloqueio de trava protegido por tryReleashared (int releases) {for (;;) {// obtenha o estado de sincronização int c = getState (); // se o estado de sincronização for 0, se (c == 0) {return false; } // Caso contrário, reduza o estado de sincronização por 1 int nextc = c-1; // Use o método CAS para atualizar o estado de sincronização se (comparaandStState (c, nextc)) {return nextc == 0; }}}}Você pode ver que o construtor da Sync definirá o valor do estado de sincronização para o valor do parâmetro aprovado. Depois disso, toda vez que o método de contagem regressiva é chamado, o valor do estado síncrono será reduzido em 1, que é o princípio da implementação do contador. Os dois métodos mais usados ao usar a classe de ferramentas Countdownlatch são o método Aguard e o método de contagem regressiva. Chamando o método aguardar bloqueará o encadeamento atual até que o contador seja 0, e chamando o método de contagem regressiva diminuirá o valor do contador em 1 até que seja reduzido para 0. Vamos dar uma olhada em como o método aguardar é chamado.
//Cause the current thread to wait until the latch decreases to 0, or the thread is interrupted public void await() throws InterruptedException { //Acquiring sync.acquireSharedInterruptibly(1);}//Acquiring lock in interruptable mode (shared mode) public final void acquireSharedInterruptibly(int arg) throws InterruptedException { //First determine whether the thread is interrupted, if so, throw an exception if (thread.interrupted ()) {tiro new interruptEdException (); } // 1. Tente adquirir o bloqueio se (TryAcquireshared (arg) <0) {// 2. Se a aquisição falhar, digite o método doacquiredarededinterruptível (arg); }}Quando o thread chama o método aguardando, ele realmente chama o método de adquirição interruptível do AQS. Este método adquire o bloqueio em resposta às interrupções do encadeamento. O código para este método também é publicado acima. Podemos ver que, no método adquirente interruptível, primeiro, ele chamará o método TryAcquiresareded para tentar adquirir o bloqueio. Vemos a lógica do método TryAcquireshared reescrito em sincronia. A lógica de implementação do método é muito simples, que é julgar se o estado de sincronização atual é 0. Se for 0, retornar 1 significa que o bloqueio pode ser adquirido, caso contrário, retornar -1 significa que o bloqueio não pode ser adquirido. Se o método TryAcquireshared retornar 1, o thread poderá continuar a executar sem esperar. Se -1 for retornado, o método doacquiredarededinterruptivelmente será chamado na fila síncrona para esperar. Este é o princípio de que chamar o método aguardar bloqueará o encadeamento atual. Vamos ver como o método de contagem regressiva acorda o tópico de bloqueio.
// Método para reduzir a trava public void Countdown () {sync.releashareded (1);} // Operação de liberação (modo compartilhado) public final boolean RELEASHARED (int arg) {// 1. Tente soltar o bloqueio se (TryReleashared (arg)) {// 2. Se a liberação for bem -sucedida, acorde outros threads doreleashared (); retornar true; } retornar false;}Você pode ver que o método RELEASHARED é chamado no método de contagem regressiva. Este método também é um método no AQS. Também publicamos seu código nele. A primeira coisa no método RELEASSEHARED é chamar o método TryReleasHareded para tentar liberar o bloqueio. O método TryReleashareded é um método abstrato no AQS. Sua lógica de implementação específica está na classe Subclass Sync. Podemos encontrar esse método no código de classe Sync publicado acima. Se o método TryReleashareared retornar verdadeiro para a liberação e retornar FALSE para liberar falhas. Só retornará verdadeiro se o estado de sincronização for exatamente 0 após diminuir 1. Em outros casos, false será retornado. Então, quando o TryReLeashareded retornar TRUE, o método DORELEASHARDARED será chamado imediatamente para acordar todos os threads na fila de sincronização. Isso explica por que a última vez que o método da contagem regressiva é chamado para reduzir o contador para 0 acordará todos os threads bloqueados. Estes são os princípios básicos do Countdownlatch. Vamos dar uma olhada em um exemplo de seu uso.
Cenário de aplicação: Ao jogar feliz proprietário, você deve esperar que os três jogadores cheguem antes de poder negociar cartas.
public class Player estende o thread {private static int count = 1; private final int id = count ++; Latcha de contagem regressiva privada; public player (CountdownLatch Latch) {this.latch = Latch; } @Override public void run () {System.out.println ("【player" + id + "] entrada"); latch.CountDown (); } public static void main (string [] args) lança interruptedException {Countdownlatch latch = new Countdownlatch (3); System.out.println ("O jogo começa, esperando o jogador entrar ..."); novo jogador (trava) .start (); novo jogador (trava) .start (); novo jogador (trava) .start (); latch.await (); System.out.println ("Os jogadores chegaram, começam a lidar ..."); }}Os resultados da operação mostram que a operação de negociação deve ser realizada depois que todos os jogadores entram em campo. Comentamos as 23 linhas travadas.await () e comparamos para ver os resultados.
Você pode ver que, depois de comentar a linha trava.await (), não é garantido que todos os jogadores comecem a negociar cartões somente depois de entrar no campo.
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.