Dans Java, le mot-clé synchronisé peut être utilisé pour le contrôle de synchronisation du thread pour obtenir un accès séquentiel aux ressources clés et éviter l'incohérence des données causée par une exécution simultanée multi-thread. Le principe de synchronisé est un moniteur d'objet (verrouillage). Seul le thread qui acquiert le moniteur peut continuer à exécuter, sinon le thread attendra pour acquérir le moniteur. Chaque objet ou classe de Java a une serrure associée. Pour un objet, il surveille la variable d'instance de cet objet. Pour une classe, il surveille la variable de classe (une classe elle-même est un objet de la classe de classe, donc le verrouillage associé à la classe est également un verrou d'objet). Il existe deux façons d'utiliser des mots clés synchronisés: la méthode synchronisée et le bloc synchronisé. Les deux zones de surveillance sont associées à un objet introduit. Lorsqu'il atteint cette zone de surveillance, le JVM verrouille l'objet de référence et lorsqu'il quitte, le verrou de l'objet de référence sera libéré (le JVM libèrera le verrou lorsqu'il y aura une sortie exception). Les verrous d'objets sont des mécanismes internes de JVM. Il vous suffit d'écrire des méthodes de synchronisation ou des blocs de synchronisation. Lors de l'exploitation des zones de surveillance, le JVM acquiert ou libérera automatiquement le verrou.
Exemple 1
Voyons d'abord le premier exemple. En Java, il n'y a qu'un seul domaine critique du même objet qui est autorisé à être accessible en même temps (toutes les méthodes synchronisées non statiques):
package concurrencée; classe publique main8 {public static void main (String [] args) {compte compte = nouveau compte (); account.setBalance (1000); Société d'entreprise = nouvelle société (compte); Thread CompanyThread = nouveau thread (société); Banque bancaire = nouvelle banque (compte); Thread BankThread = nouveau thread (banque); System.out.printf ("Compte: Solde initial:% f / n", compte.getBalance ()); CompanyThread.start (); BankThread.start (); essayez {// join () la méthode attend que ces deux threads complètent CompanyThread.Join (); BankThread.Join (); System.out.printf ("Compte: Final Solde:% f / n", account.getBalance ()); } catch (InterruptedException e) {e.printStackTrace (); }}} / * Compte * / Class Compte {double bilan privé; / * Ajouter des données entrantes à l'équilibre Balance * / public synchronisé void addamount (double quantité) {double tmp = bilan; essayez {thread.sleep (10); } catch (InterruptedException e) {e.printStackTrace (); } tmp + = montant; Balance = TMP; } / * Déduire les données entrantes de l'équilibre Balance * / public synchronisé void sustrAntAmount (double quantité) {double tmp = bilan; essayez {thread.sleep (10); } catch (InterruptedException e) {e.printStackTrace (); } tmp - = montant; Balance = TMP; } public double getBalance () {return Balance; } public void setBalance (double équilibre) {this.balance = équilibre; }} / * Banque * / Classe Banque implémente Runnable {compte de compte privé; Banque publique (compte de compte) {this.account = compte; } @Override public void run () {for (int i = 0; i <100; i ++) {account.SubtractAmount (1000); }}} / * Société * / Classe La société implémente Runnable {compte de compte privé; Compagnie publique (compte de compte) {this.account = compte; } @Override public void run () {for (int i = 0; i <100; i ++) {compte.addamount (1000); }}}Vous avez développé une demande de simulation pour les comptes bancaires qui peuvent recharger et déduire les soldes. Ce programme recharge le compte en appelant la méthode Addamount () 100 fois, en déposant 1 000 à chaque fois; Déduit ensuite le solde du compte en appelant la méthode SoustraMount () 100 fois, en déduisant 1 000 à chaque fois; Nous nous attendons à ce que le solde final du compte soit égal au solde initial, et nous l'implémentons via le mot-clé synchronisé.
Si vous souhaitez afficher le problème d'accès simultané des données partagées, vous n'avez qu'à supprimer les mots clés synchronisés dans les déclarations de méthode adddamount () et soustraire (). Sans le mot clé synchronisé, la valeur de solde imprimée n'est pas cohérente. Si vous exécutez ce programme plusieurs fois, vous obtiendrez des résultats différents. Étant donné que le JVM ne garantit pas l'ordre d'exécution des threads, chaque fois qu'il s'exécute, les threads liront et modifieront le solde du compte dans différents ordres, ce qui donne différents résultats finaux.
La méthode d'un objet est déclarée à l'aide du mot-clé synchronisé et ne peut être accessible que par un seul thread. Si le thread A exécute une méthode de synchronisation SyncMethoda (), Thread B souhaite exécuter d'autres méthodes de synchronisation SyncMethodb () de cet objet, le thread B sera bloqué jusqu'à ce que le thread A termine l'accès. Mais si le thread B accède à différents objets de la même classe, aucun thread ne sera bloqué.
Exemple 2
Démontrez le problème selon lequel des méthodes synchronisées statiques et des méthodes synchronisées non statiques sur le même objet sont accessibles par plusieurs threads en même temps. Vérifiez-le.
package concurrencée; classe publique main8 {public static void main (String [] args) {compte compte = nouveau compte (); account.setBalance (1000); Société d'entreprise = nouvelle société (compte); Thread CompanyThread = nouveau thread (société); Banque bancaire = nouvelle banque (compte); Thread BankThread = nouveau thread (banque); System.out.printf ("Compte: Solde initial:% f / n", compte.getBalance ()); CompanyThread.start (); BankThread.start (); essayez {// join () la méthode attend que ces deux threads complètent CompanyThread.Join (); BankThread.Join (); System.out.printf ("Compte: Final Solde:% f / n", account.getBalance ()); } catch (InterruptedException e) {e.printStackTrace (); }}} / * Compte * / Class Compte {/ * Modifiez-le en une variable statique ici * / Double Balance statique privé = 0; / * Ajouter des données entrantes à l'équilibre de l'équilibre, notez qu'elle est modifiée avec le void statique STATIC * / public statique Addamount (double quantité) {double tmp = équilibre; essayez {thread.sleep (10); } catch (InterruptedException e) {e.printStackTrace (); } tmp + = montant; Balance = TMP; } / * Déduire les données entrantes de l'équilibre du solde * / public synchronisé void sustractaMount (double quantité) {double tmp = bilan; essayez {thread.sleep (10); } catch (InterruptedException e) {e.printStackTrace (); } tmp - = montant; Balance = TMP; } public double getBalance () {return Balance; } public void setBalance (double équilibre) {this.balance = équilibre; }} / * Banque * / Classe Banque implémente Runnable {compte de compte privé; Banque publique (compte de compte) {this.account = compte; } @Override public void run () {for (int i = 0; i <100; i ++) {account.SubtractAmount (1000); }}} / * Société * / Classe La société implémente Runnable {compte de compte privé; Compagnie publique (compte de compte) {this.account = compte; } @Override public void run () {for (int i = 0; i <100; i ++) {compte.addamount (1000); }}}Je viens d'ajouter le mot-clé statique pour modifier l'équilibre dans l'exemple précédent, et la méthode adddamount () peut également modifier le mot-clé statique. Vous pouvez tester les résultats d'exécution vous-même et chaque exécution aura un résultat différent!
Un résumé: