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); } }
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(); } }
余额不足 账户余额: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); } }
余额不足 账户余额:0 余额不足 账户余额:0 1441790837380存进:100 账户余额:100 1441790838380取出:100 账户余额:0 1441790838380存进:100 账户余额:100 1441790839381取出:100 账户余额:0
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
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!