CountdownLatch est une classe d'outils utile. L'utilisation, nous pouvons intercepter un ou plusieurs threads pour s'exécuter après une certaine condition est mûre. Son contenu interne fournit un compteur et la valeur initiale du compteur doit être spécifiée lors de la construction d'un verrou et la valeur initiale du compteur doit être supérieure à 0. De plus, il fournit également une méthode de compte à rebours pour faire fonctionner la valeur du compteur. Le compteur sera décrémenté de 1 chaque fois que la méthode du compte à rebours est appelée. Lorsque la valeur de comptoir est réduite à 0, cela signifie que les conditions sont mûres et que tous les fils bloqués en appelant la méthode Await seront éveillés. Il s'agit du mécanisme interne de CountdownLatch. Cela semble très simple, ce n'est rien de plus que de bloquer certains fils et de leur permettre d'exécuter après avoir atteint une certaine condition. Cependant, CountdownLatch propose une large gamme de scénarios d'application. Tant que vous avez un grand esprit et que vous l'utilisez, vous pouvez jouer divers trucs. Le scénario d'application le plus courant consiste à permettre à plusieurs threads d'exécuter une tâche en même temps, puis à compter et résumer les résultats après l'exécution de toutes les tâches. La figure suivante montre dynamiquement l'ensemble du processus de blocage des threads.
La figure ci-dessus montre que 5 threads sont bloqués en appelant la méthode Await, et ils doivent attendre que la valeur de comptoir diminue à 0 avant de continuer à s'exécuter. La valeur initiale du compteur est spécifiée lors de la construction d'un verrou et est par la suite réduite de 1 avec chaque appel au compte à rebours. Le code suivant publie la méthode de construction à compte à rebours.
// Constructeur public CountdownLatch (int count) {if (count <0) lance un nouveau IllégalArgumentException ("Count <0"); this.Sync = new sync (count); }CountdownLatch n'a qu'un seul constructeur de paramètres, et une valeur supérieure à 0 doit être transmise car la valeur initiale du compteur, sinon une erreur sera signalée. Vous pouvez voir que dans le constructeur, juste un nouvel objet de synchronisation et l'attribuer à la synchronisation de la variable membre. Comme d'autres classes d'outils de synchronisation, l'implémentation de CountdownLatch repose sur AQS, qui est une application en mode partagé AQS. CountdownLatch implémente une synchronisation de classe interne et l'utilise pour hériter des AQ, de sorte que la plupart des méthodes fournies par AQS peuvent être utilisées. Jetons un coup d'œil au code de la classe interne de synchronisation.
// Synchronizer la classe finale statique privée Sync étend AbstractQueuedSynchronizer {// Constructor Sync (int count) {setState (count); } // Obtenez l'état de synchronisation actuel int getCount () {return getState (); } // Essayez d'obtenir le numéro négatif de verrouillage // Retour: indique que le thread actuel n'a pas obtenu // la valeur zéro retour: indique que le thread actuel a été acquis avec succès, mais le thread ultérieur ne peut plus obtenir // Renter un nombre positif: indique que le thread actuel a été acquis avec succès, et le thread sous-suivant peut également obtenir le succès de la réussite INT TRYACQUIRARE (INT COMPRESSE) {return (GetState () == 0)? 1: -1; } // Essayez de libérer le verrouillage Boolean TryReleSeshared (INT releases) {for (;;) {// Obtenez l'état de synchronisation int c = getState (); // Si l'état de synchronisation est 0, if (c == 0) {return false; } // sinon, réduisez l'état de synchronisation par 1 int nextc = c-1; // Utilisez la méthode CAS pour mettre à jour l'état de synchronisation if (comparabledSetState (c, nextc)) {return nextc == 0; }}}}Vous pouvez voir que le constructeur de Sync définira la valeur de l'état de synchronisation à la valeur du paramètre passée. Après cela, chaque fois que le compte à rebours est appelé, la valeur de l'état synchrone sera réduite de 1, qui est le principe de mise en œuvre du compteur. Les deux méthodes les plus couramment utilisées lors de l'utilisation de la classe d'outils CountdownLatch sont la méthode Await et la méthode du compte à rebours. L'appel de la méthode Await bloquera le thread actuel jusqu'à ce que le compteur soit 0, et l'appel de la méthode de compte à rebours diminuera la valeur du compteur de 1 jusqu'à ce qu'elle soit réduite à 0. Jetons un coup d'œil à la façon dont la méthode Await est appelée.
// a fait que le thread actuel attend jusqu'à ce que le verrou diminue à 0, ou que le thread est interrompu public void attend () lance l'interruption Exception if (Thread.Interrupted ()) {Throw New InterruptedException (); } // 1. Essayez d'acquérir le verrou si (tryacQuireShared (arg) <0) {// 2. Si l'acquisition échoue, entrez la méthode DOACQUESHAREDARETRUBLIBLE (ARG); }}Lorsque le thread appelle la méthode Await, il appelle en fait la méthode acquiseRaredAndiquablement des AQ. Cette méthode acquiert la serrure en réponse aux interruptions du thread. Le code de cette méthode est également publié ci-dessus. Nous pouvons voir que dans la méthode acquiseRaredAndertively, il appellera d'abord la méthode TryAcquireRared pour essayer d'acquérir le verrou. Nous voyons la logique de la méthode TryAcQuirsaredred réécrite en synchronisation. La logique d'implémentation de la méthode est très simple, qui consiste à juger si l'état de synchronisation actuel est 0. S'il est 0, le retour 1 signifie que la serrure peut être acquise, sinon le retour -1 signifie que le verrou ne peut être acquis. Si la méthode TryAcQuirsared renvoie 1, le thread peut continuer à s'exécuter sans attendre. Si -1 est renvoyé, la méthode DoacQuirsaredAredIndemly sera appelée dans la file d'attente synchrone pour attendre. C'est le principe que l'appel de la méthode Await bloquera le thread actuel. Voyons comment le compte à rebours réveille le fil de blocage.
// Méthode pour réduire le comptoir public de verrouillage public () {sync.reaseshared (1);} // Opération de libération (mode partagé) public final booléen releaseshared (int arg) {// 1. Essayez de libérer le verrou si (tryreleSeshared (arg)) {// 2. Si la version réussit, réveillez-vous d'autres fils DoreleSeshared (); Retour Vrai; } return false;}Vous pouvez voir que la méthode de relance est appelée dans la méthode du compte à rebours. Cette méthode est également une méthode dans AQS. Nous avons également publié son code dessus. La première chose dans la méthode de relance est d'appeler la méthode TryReleSeshared pour essayer de libérer le verrou. La méthode TryReleSeshared est une méthode abstraite dans AQS. Sa logique d'implémentation spécifique se trouve dans la classe de synchronisation des sous-classes. Nous pouvons trouver cette méthode dans le code de classe de synchronisation publié ci-dessus. Si la méthode TryReleSeshared renvoie True à la libération et renvoie False à l'échec de la libération. Il ne reviendra que si l'état de synchronisation est exactement 0 après avoir diminué 1. Dans d'autres cas, FALSE sera retourné. Ensuite, lorsque TryReleSeshared renvoie true, la méthode DoreleSeshared sera immédiatement appelée pour réveiller tous les threads de la file d'attente de synchronisation. Cela explique pourquoi la dernière fois que la méthode de compte à rebours est appelée pour réduire le compteur à 0 se réveillera tous les threads bloqués. Ce sont les principes de base du compte à rebours. Jetons un coup d'œil à un exemple de son utilisation.
Scénario d'application: lorsque vous jouez à Happy Landlord, vous devez attendre que les trois joueurs arrivent avant de pouvoir traiter les cartes.
Le joueur de classe publique étend Thread {private static int count = 1; private final int id = count ++; Latch à compte à rebours privé; Public Player (CountdownLatch Latch) {this.latch = latch; } @Override public void run () {System.out.println ("【lecteur" + id + "] entrée"); latch.CountDown (); } public static void main (String [] args) lève InterruptedException {CountdownLatch latch = new CountdownLatch (3); System.out.println ("Le jeu commence, en attendant que le joueur entre ..."); Nouveau joueur (Latch) .start (); Nouveau joueur (Latch) .start (); Nouveau joueur (Latch) .start (); latch.Await (); System.out.println ("Les joueurs sont arrivés, commencent à traiter ..."); }}Les résultats de l'opération montrent que l'opération de trafic doit être effectuée après que tous les joueurs entrent sur le terrain. Nous commençons les 23 lignes Latch.Await () et le comparons pour voir les résultats.
Vous pouvez voir qu'après avoir commenté la ligne Latch.Await (), il n'est pas garanti que tous les joueurs commenceront à traiter les cartes qu'après être entrés dans le champ.
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.