Maison > Java > javaDidacticiel > le corps du texte

Exemple de démonstration de Java utilisant la méthode modifiée synchronisée pour synchroniser les threads

高洛峰
Libérer: 2017-01-05 15:34:08
original
1466 Les gens l'ont consulté

Vous pouvez utiliser le mot-clé synchronisé en Java pour le contrôle de la synchronisation des threads afin d'obtenir un accès séquentiel aux ressources clés et d'éviter des problèmes tels que l'incohérence des données causée par l'exécution simultanée de plusieurs threads. Le principe de synchronisé est le moniteur d'objet (lock). Seul le thread qui obtient le moniteur peut continuer à s'exécuter, sinon le thread attendra d'obtenir le moniteur. Chaque objet ou classe en Java est associé à un verrou. Pour un objet, ce qui est surveillé est la variable d'instance de l'objet. Pour une classe, ce qui est surveillé est la variable de classe (une classe elle-même est un objet de classe Class, Le verrou associé à la classe est donc également un verrou d'objet). Il existe deux manières d'utiliser le mot-clé synchronisé : la méthode synchronisée et le bloc synchronisé. Les deux zones de surveillance sont associées à un objet introduit. En arrivant dans cette zone de surveillance, la JVM verrouillera l'objet de référence et libérera le verrou sur l'objet de référence en quittant (lorsqu'il y a une sortie d'exception, la JVM libérera le verrou.) . Le verrouillage des objets est un mécanisme interne de la JVM. Il vous suffit d'écrire une méthode de synchronisation ou un bloc de synchronisation lors de l'utilisation de la zone de surveillance, la JVM acquerra ou libérera automatiquement le verrou.

Exemple 1

Regardons d'abord le premier exemple. En Java, une seule section critique du même objet est autorisée à être accessible en même temps (toutes sont des méthodes synchronisées non statiques). ):

package concurrency;
 
public class Main8 {
  public static void main(String[] args) {
    Account account = new Account();
    account.setBalance(1000);
    Company company = new Company(account);
    Thread companyThread = new Thread(company);
    Bank bank = new Bank(account);
    Thread bankThread = new Thread(bank);
    System.out.printf("Account : Initial Balance: %f\n", account.getBalance());
    companyThread.start();
    bankThread.start();
    try {
      //join()方法等待这两个线程运行完成
      companyThread.join();
      bankThread.join();
      System.out.printf("Account : Final Balance: %f\n", account.getBalance());
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}
Copier après la connexion
Copier après la connexion
/*帐户*/
class Account{
  private double balance;
  /*将传入的数据加到余额balance中*/
  public synchronized void addAmount(double amount){
    double tmp = balance;
    try {
      Thread.sleep(10);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    tmp += amount;
    balance = tmp;
  }
  /*将传入的数据从余额balance中扣除*/
  public synchronized void subtractAmount(double amount){
    double tmp = balance;
    try {
      Thread.sleep(10);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    tmp -= amount;
    balance = tmp;
  }
  public double getBalance() {
    return balance;
  }
  public void setBalance(double balance) {
    this.balance = balance;
  }
}
Copier après la connexion
/*银行*/
class Bank implements Runnable{
  private Account account;
  public Bank(Account account){
    this.account = account;
  }
  @Override
  public void run() {
    for (int i = 0; i < 100; i++) {
      account.subtractAmount(1000);
    }
  }
}
Copier après la connexion
Copier après la connexion
/*公司*/
class Company implements Runnable{
  private Account account;
  public Company(Account account){
    this.account = account;
  }
  @Override
  public void run() {
    for (int i = 0; i < 100; i++) {
      account.addAmount(1000);
    }
  }
}
Copier après la connexion
Copier après la connexion

Vous avez développé une application de compte bancaire simulé qui peut 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 ; puis déduit le solde du compte en appelant la méthode subtractAmount() 100 fois, en déduisant 1 000 à chaque fois ; le même que le solde initial L'égalité est obtenue grâce au mot-clé synchronisé.

Si vous souhaitez vérifier le problème d'accès simultané aux données partagées, il vous suffit de supprimer le mot-clé synchronisé dans les déclarations des méthodes addAmount() et subtractAmount(). Sans le mot-clé synchronisé, les valeurs de solde imprimées sont incohérentes. Si vous exécutez ce programme plusieurs fois, vous obtiendrez des résultats différents. Étant donné que la JVM ne garantit pas l'ordre d'exécution des threads, les threads liront et modifieront le solde du compte dans un ordre différent à chaque exécution, ce qui entraînera un résultat final différent.

La méthode d'un objet est déclarée à l'aide du mot-clé synchronisé et n'est accessible que par un seul thread. Si le thread A exécute une méthode de synchronisation syncMethodA() et que le 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 des deux threads ne sera bloqué.

Exemple 2

Montre le problème selon lequel les méthodes synchronisées statiques et les méthodes synchronisées non statiques sur le même objet sont accessibles par plusieurs threads au même moment. Vérifiez-le.

package concurrency;
 
public class Main8 {
  public static void main(String[] args) {
    Account account = new Account();
    account.setBalance(1000);
    Company company = new Company(account);
    Thread companyThread = new Thread(company);
    Bank bank = new Bank(account);
    Thread bankThread = new Thread(bank);
    System.out.printf("Account : Initial Balance: %f\n", account.getBalance());
    companyThread.start();
    bankThread.start();
    try {
      //join()方法等待这两个线程运行完成
      companyThread.join();
      bankThread.join();
      System.out.printf("Account : Final Balance: %f\n", account.getBalance());
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}
Copier après la connexion
Copier après la connexion
/*帐户*/
class Account{
  /*这里改为静态变量*/
  private static double balance = 0;
  /*将传入的数据加到余额balance中,注意是用static修饰过的*/
  public static synchronized void addAmount(double amount){
    double tmp = balance;
    try {
      Thread.sleep(10);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    tmp += amount;
    balance = tmp;
  }
  /*将传入的数据从余额balance中扣除*/
  public synchronized void subtractAmount(double amount){
    double tmp = balance;
    try {
      Thread.sleep(10);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    tmp -= amount;
    balance = tmp;
  }
  public double getBalance() {
    return balance;
  }
  public void setBalance(double balance) {
    this.balance = balance;
  }
}
Copier après la connexion
/*银行*/
class Bank implements Runnable{
  private Account account;
  public Bank(Account account){
    this.account = account;
  }
  @Override
  public void run() {
    for (int i = 0; i < 100; i++) {
      account.subtractAmount(1000);
    }
  }
}
Copier après la connexion
Copier après la connexion
/*公司*/
class Company implements Runnable{
  private Account account;
  public Company(Account account){
    this.account = account;
  }
  @Override
  public void run() {
    for (int i = 0; i < 100; i++) {
      account.addAmount(1000);
    }
  }
}
Copier après la connexion
Copier après la connexion

Je viens de modifier le solde dans l'exemple précédent en ajoutant le mot-clé static. La méthode addAmount() peut également être modifiée avec le mot-clé static. Vous pouvez tester vous-même les résultats de l'exécution. Chaque exécution aura des résultats différents !

Quelque résumé :

synchronisé est implémenté via un logiciel (JVM) et est simple et facile à utiliser Même avec Lock après JDK5, il est encore largement utilisé.

La synchronisation est en fait injuste. Les nouveaux threads peuvent obtenir le moniteur immédiatement, et les threads qui attendent dans la zone d'attente depuis longtemps peuvent attendre à nouveau. Cependant, cette méthode de préemption peut éviter la famine.

Les verrous synchronisés ne sont associés qu'à une seule condition (si acquérir le verrou) et sont inflexibles. Plus tard, la combinaison de Condition et de Verrouillage a résolu ce problème.

Lorsque plusieurs threads se disputent un verrou, les threads restants qui n'ont pas obtenu le verrou ne peuvent que continuer à essayer d'obtenir le verrou sans interruption. Une concurrence élevée entraînera une dégradation des performances. La méthode lockInterruptably() de ReentrantLock peut donner la priorité à la réponse aux interruptions. Si un thread attend trop longtemps, il peut s'interrompre, puis ReentrantLock répond à l'interruption et ne laisse plus le thread continuer à attendre. Avec ce mécanisme, lors de l'utilisation de ReentrantLock, il n'y aura pas de blocage comme la synchronisation.

Pour plus d'exemples et de démonstrations de Java utilisant la méthode de modification synchronisée pour synchroniser les threads, veuillez faire attention au site Web PHP chinois !


Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal