Maison >Java >javaDidacticiel >Introduction à plusieurs méthodes de synchronisation multithread Java
Cet article présente principalement des informations pertinentes sur plusieurs méthodes de synchronisation des multi-threads Java. Voici 5 méthodes. Les amis dans le besoin peuvent se référer à
Plusieurs méthodes de synchronisation des multi-threads Java <.>
1. Introduction
2. Pourquoi la synchronisation des threads est nécessaire
3. Code lorsqu'il n'est pas synchronisé
package threadTest; /** * @author ww * */ public class Bank { private int count =0;//账户余额 //存钱 public void addMoney(int money){ count +=money; System.out.println(System.currentTimeMillis()+"存进:"+money); } //取钱 public void subMoney(int money){ if(count-money < 0){ System.out.println("余额不足"); return; } count -=money; System.out.println(+System.currentTimeMillis()+"取出:"+money); } //查询 public void lookMoney(){ System.out.println("账户余额:"+count); } }SyncThreadTest . java
package threadTest; /** * Java学习交流QQ群:589809992 我们一起学Java! */ public class SyncThreadTest { public static void main(String args[]){ final Bank bank=new Bank(); Thread tadd=new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } bank.addMoney(100); bank.lookMoney(); System.out.println("\n"); } } }); Thread tsub = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub while(true){ bank.subMoney(100); bank.lookMoney(); System.out.println("\n"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }); tsub.start(); tadd.start(); } }Le code est très simple, donc je ne vais pas l'expliquer. Voyons à quoi ressemblent les résultats ? J'en ai coupé une partie. C'est très compliqué et je ne comprends pas.
余额不足 账户余额:0 余额不足 账户余额:100 1441790503354存进:100 账户余额:100 1441790504354存进:100 账户余额:100 1441790504354取出:100 账户余额:100 1441790505355存进:100 账户余额:100 1441790505355取出:100 账户余额:100
4. Code lors de l'utilisation de la synchronisation
Bank.java modifié
package threadTest; /** * @author ww * */ public class Bank { private int count =0;//账户余额 //存钱 public synchronized void addMoney(int money){ count +=money; System.out.println(System.currentTimeMillis()+"存进:"+money); } //取钱 public synchronized void subMoney(int money){ if(count-money < 0){ System.out.println("余额不足"); return; } count -=money; System.out.println(+System.currentTimeMillis()+"取出:"+money); } //查询 public void lookMoney(){ System.out.println("账户余额:"+count); } }Regardez les résultats en cours :
余额不足 账户余额:0 余额不足 账户余额:0 1441790837380存进:100 账户余额:100 1441790838380取出:100 账户余额:0 1441790838380存进:100 账户余额:100 1441790839381取出:100 账户余额:0I j'ai l'impression de pouvoir le comprendre instantanément.
C'est-à-dire le bloc d'instructions modifié par le mot-clé synchronisé. Le bloc de relevé modifié par ce mot-clé sera automatiquement ajouté avec un verrou intégré pour réaliser la synchronisation.
Le code Bank.java est le suivant :
Les résultats d'exécution sont les suivants :
package threadTest; /** * @author ww * */ public class Bank { private int count =0;//账户余额 //存钱 public void addMoney(int money){ synchronized (this) { count +=money; } System.out.println(System.currentTimeMillis()+"存进:"+money); } //取钱 public void subMoney(int money){ synchronized (this) { if(count-money < 0){ System.out.println("余额不足"); return; } count -=money; } System.out.println(+System.currentTimeMillis()+"取出:"+money); } //查询 public void lookMoney(){ System.out.println("账户余额:"+count); } }
L'effet est similaire à la première méthode.
余额不足 账户余额:0 1441791806699存进:100 账户余额:100 1441791806700取出:100 账户余额:0 1441791807699存进:100 账户余额:100
Remarque : La synchronisation est une opération très coûteuse, le contenu synchronisé doit donc être minimisé. Habituellement, il n'est pas nécessaire de synchroniser l'intégralité de la méthode, utilisez simplement des blocs de code synchronisés pour synchroniser le code clé.
Bank.java est le suivant :
Quel est l'effet de course. ?
package threadTest; /** * @author ww * */ public class Bank { private volatile int count = 0;// 账户余额 // 存钱 public void addMoney(int money) { count += money; System.out.println(System.currentTimeMillis() + "存进:" + money); } // 取钱 public void subMoney(int money) { if (count - money < 0) { System.out.println("余额不足"); return; } count -= money; System.out.println(+System.currentTimeMillis() + "取出:" + money); } // 查询 public void lookMoney() { System.out.println("账户余额:" + count); } }
Êtes-vous encore confus ? Pourquoi est-ce ? En effet, volatile ne peut pas garantir les opérations atomiques, donc volatile ne peut pas remplacer synchronisé. De plus, volatile empêchera le compilateur d'optimiser le code, donc si vous ne pouvez pas l'utiliser, ne l'appliquez pas. Son principe est que chaque fois qu'un thread veut accéder à une variable volatile modifiée, il la lit dans la mémoire au lieu de la lire dans le cache, donc la valeur de la variable accédée par chaque thread est la même. Cela garantit la synchronisation.
余额不足 账户余额:0 余额不足 账户余额:100 1441792010959存进:100 账户余额:100 1441792011960取出:100 账户余额:0 1441792011961存进:100 账户余额:100(4) Utilisez des verrous de réentrance pour réaliser la synchronisation des threads Un nouveau package java.util.concurrent a été ajouté dans JavaSE5.0 pour prendre en charge la synchronisation. La classe ReentrantLock est un verrou réentrant et mutuellement exclusif qui implémente l'interface Lock. Il a le même comportement de base et la même sémantique que l'utilisation de méthodes et de blocs synchronisés, et étend ses capacités. Les méthodes couramment utilisées de la classe ReentrantLock sont : ReentrantLock() : Créer une instance ReentrantLock lock() : Obtenir le verrou unlock() : Libérer le verrou Remarque : ReentrantLock() possède également un constructeur qui peut créer un verrou équitable, mais parce qu'il peut réduire considérablement le programme Pour l'efficacité opérationnelle, il n'est pas recommandé d'utiliser la modification du code Bank.java comme suit :
Quel est l'effet de fonctionnement ?
package threadTest; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author ww * */ public class Bank { private int count = 0;// 账户余额 //需要声明这个锁 private Lock lock = new ReentrantLock(); // 存钱 public void addMoney(int money) { lock.lock();//上锁 try{ count += money; System.out.println(System.currentTimeMillis() + "存进:" + money); }finally{ lock.unlock();//解锁 } } // 取钱 public void subMoney(int money) { lock.lock(); try{ if (count - money < 0) { System.out.println("余额不足"); return; } count -= money; System.out.println(+System.currentTimeMillis() + "取出:" + money); }finally{ lock.unlock(); } } // 查询 public void lookMoney() { System.out.println("账户余额:" + count); } }
L'effet est similaire aux deux premières méthodes.
余额不足 账户余额:0 余额不足 账户余额:0 1441792891934存进:100 账户余额:100 1441792892935存进:100 账户余额:200 1441792892954取出:100 账户余额:100
Si le mot-clé synchronisé peut répondre aux besoins de l'utilisateur, utilisez synchronisé car il peut simplifier le code. Si vous avez besoin de fonctions plus avancées, utilisez la classe ReentrantLock. À ce stade, faites attention à libérer le verrou à temps, sinon un blocage se produira généralement dans le code final
Le code Bank.java est le suivant :
Effet d'exécution :
package threadTest; /** * @author ww * */ public class Bank { private static ThreadLocal<Integer> count = new ThreadLocal<Integer>(){ @Override protected Integer initialValue() { // TODO Auto-generated method stub return 0; } }; // 存钱 public void addMoney(int money) { count.set(count.get()+money); System.out.println(System.currentTimeMillis() + "存进:" + money); } // 取钱 public void subMoney(int money) { if (count.get() - money < 0) { System.out.println("余额不足"); return; } count.set(count.get()- money); System.out.println(+System.currentTimeMillis() + "取出:" + money); } // 查询 public void lookMoney() { System.out.println("账户余额:" + count.get()); } }
Écoutez, j'étais confus quant à l'effet de l'opération au début. Pourquoi est-il uniquement autorisé à enregistrer mais pas à retirer ? Jetez un œil au principe de ThreadLocal :
余额不足 账户余额:0 余额不足 账户余额:0 1441794247939存进:100 账户余额:100 余额不足 1441794248940存进:100 账户余额:0 账户余额:200 余额不足 账户余额:0 1441794249941存进:100 账户余额:300
Si vous utilisez ThreadLocal pour gérer les variables, chaque thread qui utilise la variable obtiendra une copie de la variable. Les copies sont indépendantes les unes des autres, afin que chaque thread puisse modifier sa propre copie de la variable à volonté sans en provoquer. impact sur d’autres fils de discussion. Maintenant que vous comprenez, il s'avère que chaque thread exécute une copie, ce qui signifie que déposer de l'argent et retirer de l'argent sont deux comptes avec le même nom de connaissance. L’effet ci-dessus se produira donc.
ThreadLocal et mécanisme de synchronisation
a. ThreadLocal et le mécanisme de synchronisation servent tous deux à résoudre le problème des conflits d'accès aux mêmes variables dans les multi-threads b. méthode « espace contre temps », cette dernière adopte la méthode « d'échange de temps contre espace »
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!