Comment synchroniser méthodes pour utiliser des fils java

Chaque fois que vous travaillez sur un programme Java qui utilise les threads, vous devez tenir compte de la question de la concomitance méchant. En particulier, si deux threads tentent d'accéder à une méthode d'un objet exactement au même moment? Sauf si vous programmez soigneusement, le résultat peut être désastreux. Une méthode qui effectue un calcul simple retourne des résultats inexacts.

Dans une application de services bancaires en ligne, vous pourriez découvrir que certains dépôts sont crédités deux fois et certains retraits ne sont pas crédités à tous. Dans un système de commande en ligne, l'ordre d'un client pourrait obtenir enregistré dans le compte d'un client différent.

La clé de la gestion des problèmes de concurrence est reconnaissant méthodes que les données de mise à jour et que l'on pourrait appeler de plus d'un fil. Après avoir identifié ces méthodes, la solution est simple. Il suffit d'ajouter le synchronisé mot-clé pour la déclaration de méthode, comme ceci:

someMethod public synchronized void () ...

Ce code indique Java pour placer un bloquer sur l'objet de telle sorte que pas d'autres méthodes peuvent appeler d'autres méthodes synchronisés pour l'objet jusqu'à ce que cette méthode se termine. En d'autres termes, il désactive temporairement le multithreading pour l'objet.

Voici quelques exemples concrets. Ce code crée une instance de la CountDownClock classe.

DoTwoThings de classe importation java.util.concurrent.ScheduledThreadPoolExecutor publiques {piscine ScheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor (2) -CountDownClock horloge = new CountDownClock (20) -publics static void main (String [] args) {nouveaux DoTwoThings () -} (DoTwoThings ) {pool.execute (horloge) -pool.execute (horloge) -pool.shutdown () -}}

La sortie résultante est un méli-mélo imprévisible des sorties de deux fils, avec certains chefs d'accusation dupliqués et d'autres entièrement sauté, comme ceci:

T moins 20T moins 20T moins 19T moins 19T moins 18T moins 17T moins 16T moins 15T moins 13T moins 13T moins 12T moins 12T moins 11T moins 11T moins 10T moins 9T moins 7T moins 7T moins 6T moins 5T moins 4T moins 3T moins 2T moins 2T moins 1T moins 0

Les deux fils exécutent leurs boucles simultanément, donc après un thread affiche son T moins 20, l'autre thread affiche son propre T moins 20. La même chose se produit pour T moins 19, T moins 18, et ainsi de suite.




Puis ce code engendre deux fils, dont chacun exécute une copie de la CountDownClock le code exemple.

DoTwoThingsSync {piscine ScheduledThreadPoolExecutor classe importation java.util.concurrent.ScheduledThreadPoolExecutor public = new ScheduledThreadPoolExecutor (2) -CountDownClockSync horloge = new CountDownClockSync (20) -publics static void main (String [] args) {nouvelle DoTwoThingsSync () -} DoTwoThingsSync ( ) {pool.execute (horloge) -pool.execute (horloge) -pool.shutdown () -}}

Java de synchronisé mot-clé assure que seul un thread à la fois appelle la courir Procédé. Le résultat obtenu montre une exécution complète de la courir méthode suivie par un autre.

classe CountDownClockSync extends Thread {private int start-publique CountDownClockSync (int start) {} this.start = démarrage synchronisé d'exécution public void () {for (int t = démarrage t> = 0- t -) {System.out .println ("T moins" + t), essayez d'{Thread.sleep (1000) -} catch (InterruptedException e) {}}}}

Les appels vers le fils de deux courir méthode ne sont pas entrelacées, de sorte que les chiffres de sortie vers le bas de 20 à 0 et compte puis une seconde fois à partir de 20 à 0:

T moins 20T moins 19T moins 18

Et ainsi de suite, jusqu'à

T moins 2T 1T moins moins moins 0T 20T moins 19T moins 18

Et ainsi de suite, jusqu'à

T moins 2T moins 1T moins 0

La partie la plus difficile est de savoir quelles sont les méthodes pour synchroniser. Toute méthode qui met à jour les variables d'instance est à risque - et doit être synchronisée. Cela est parce que quand deux ou plusieurs threads exécuter une méthode dans le même temps, les discussions ont une copie commune des variables d'instance de la méthode.

Même les méthodes qui consistent en une seule ligne de code sont à risque. Considérez cette méthode:

int sequenceNumber = 0-public int getNextSequenceNumber () {return sequenceNumber ++ -}

On pourrait penser que parce que cette méthode a juste une déclaration, un autre thread ne pouvait pas l'interrompre au milieu. Hélas, ce ne est pas le cas. Cette méthode doit obtenir la valeur de la numéro de séquence champ, ajouter 1 à elle, enregistrer la valeur mise à jour vers le numéro de séquence champ, et retourner la valeur.

En fait, cette déclaration Java simple compile 11 instructions de bytecode. Si le fil est préempté entre l'un de ces bytecode par un autre thread appelant la même méthode, les numéros de série se déteriorés.

Pour des raisons de sécurité, pourquoi ne pas simplement faire toutes les méthodes synchronisées? Vous avez deux raisons de ne pas le faire:

  • Synchronisation méthodes prend du temps. Java doit acquérir un verrou sur l'objet étant synchronisé, exécuter la méthode, puis libérer le verrou. Mais avant qu'il ne puisse le faire, il doit vérifier pour vous assurer que un autre thread ne possède pas déjà un verrou sur l'objet. Tout ce travail prend du temps.

  • Plus important encore, la synchronisation de toutes vos méthodes encontre de l'objectif du multithreading, vous devriez donc synchroniser seulement les méthodes qui en ont besoin.

La synchronisé mot-clé ne bloque pas tous accès à un objet. Autres threads peuvent toujours courir méthodes non synchronisées de l'objet alors que l'objet est verrouillé.

La Objet classe fournit trois méthodes qui peuvent laisser des objets synchronisés coordonnent leurs activités. La attendez méthode place un thread dans l'état d'attente jusqu'à ce qu'un autre thread appelle soit l'objet de notifier ou (plus souvent) notifyAll Procédé. Ces méthodes sont utiles quand un thread doit attendre un autre thread de faire quelque chose avant qu'il puisse continuer.

L'exemple classique est un système bancaire dans lequel un thread effectue des retraits et l'autre fait des dépôts. Si le solde du compte d'un client tombe à zéro, le fil qui effectue des retraits peuvent appeler attendez- puis le fil qui fait des dépôts peuvent appeler notifyAll. De cette façon, à chaque fois qu'un dépôt est effectué, le fil de retrait peut revérifier le solde du compte du client pour voir si elle a maintenant assez d'argent.


» » » » Comment synchroniser méthodes pour utiliser des fils java