Maison >Java >javaDidacticiel >Utilisation du mot-clé java synchronisé
0. Code de question principale
Le code suivant montre un compteur, et deux threads effectuent une opération d'accumulation sur i en même temps, chacun s'exécutant 1 000 000 fois. Le résultat que nous attendons est définitivement i=2000000. Mais après l'avoir exécuté plusieurs fois, nous constaterons que la valeur de i est toujours inférieure à 2 000 000. En effet, lorsque deux threads écrivent sur i en même temps, le résultat d'un thread écrasera l'autre
<.>public class AccountingSync implements Runnable { static int i = 0; public void increase() { i++; } @Override public void run() { for (int j = 0; j < 1000000; j++) { increase(); } } public static void main(String[] args) throws InterruptedException { AccountingSync accountingSync = new AccountingSync(); Thread t1 = new Thread(accountingSync); Thread t2 = new Thread(accountingSync); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }Pour résoudre fondamentalement ce problème, nous devons nous assurer que plusieurs threads sont complètement synchronisés lorsqu'ils fonctionnent sur i. En d'autres termes, le thread A écrit sur i lors de la synchronisation. Le travail consiste à verrouiller le code synchronisé afin qu'un seul thread puisse entrer dans le bloc de synchronisation à la fois, assurant ainsi la sécurité entre les threads. Tout comme dans le code ci-dessus, l'opération de i ne peut être effectuée que simultanément. Un thread est en cours d'exécution.
agit directement sur la méthode instance : cela équivaut à verrouiller l'instance courante. La saisie du bloc de code de synchronisation nécessite l'obtention du verrou de l'instance courante (cela nécessite d'utiliser la même instance Runnable lors de la création du Thread)
Agit directement sur le statique. méthodes : équivalent au verrouillage de la classe actuelle. Avant d'entrer dans le bloc de code synchronisé, vous devez obtenir le verrou de la classe actuelle
2.1 Verrouiller l'objet spécifié
Le code suivant s'applique de manière synchronisée. à un objet donné. Une chose à noter ici est que l'objet donné doit être statique, sinon, chaque fois que nous créons un nouveau thread, l'objet ne sera pas partagé entre eux et la signification du verrouillage est également différente. n'existe plus.
public class AccountingSync implements Runnable { final static Object OBJECT = new Object(); static int i = 0; public void increase() { i++; } @Override public void run() { for (int j = 0; j < 1000000; j++) { synchronized (OBJECT) { increase(); } } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new AccountingSync()); Thread t2 = new Thread(new AccountingSync()); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }
2.2 Agir directement sur les méthodes d'instance
Le mot-clé synchronisé agit sur les méthodes d'instance, ce qui signifie qu'avant de saisir la méthode Increase(), le thread doit obtenir le verrou de l'instance actuelle. Cela nous oblige à utiliser la même instance d'objet Runnable lors de la création d'une instance de Thread. les verrous des threads ne sont pas sur la même instance, et il n'y a aucun moyen de parler de problèmes de verrouillage/synchronisation
Trois lignes, expliquant l'utilisation correcte des mots-clés sur les méthodes d'instance
<.>
public class AccountingSync implements Runnable { static int i = 0; public synchronized void increase() { i++; } @Override public void run() { for (int j = 0; j < 1000000; j++) { increase(); } } public static void main(String[] args) throws InterruptedException { AccountingSync accountingSync = new AccountingSync(); Thread t1 = new Thread(accountingSync); Thread t2 = new Thread(accountingSync); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }
3. verrouillage
De l'exemple ci-dessus, nous savons que si nous avons besoin d'une application de compteur, afin de garantir l'exactitude des données, nous aurons naturellement besoin de Le compteur est verrouillé, nous pouvons donc écrire le code suivant :
public class AccountingSync implements Runnable { static int i = 0; public static synchronized void increase() { i++; } @Override public void run() { for (int j = 0; j < 1000000; j++) { increase(); } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new AccountingSync()); Thread t2 = new Thread(new AccountingSync()); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }
Lorsque nous exécutons le code ci-dessus, nous constaterons que la sortie i est très petite. Cela montre que les threads ne sont pas sûrs
.Pour expliquer ce problème, il faut commencer par Integer : En Java, Integer est un objet immuable. Comme String, une fois l'objet créé, il ne peut pas être modifié. Si vous avez un Integer=1, alors il le sera. soit toujours 1. Et si vous voulez que cet objet = 2 ? Vous ne pouvez recréer qu'un Integer. Après chaque i, cela équivaut à appeler la méthode valueOf d'Integer.
public class BadLockOnInteger implements Runnable { static Integer i = 0; @Override public void run() { for (int j = 0; j < 1000000; j++) { synchronized (i) { i++; } } } public static void main(String[] args) throws InterruptedException { BadLockOnInteger badLockOnInteger = new BadLockOnInteger(); Thread t1 = new Thread(badLockOnInteger); Thread t2 = new Thread(badLockOnInteger); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }
Integer.valueOf() est en fait une méthode d'usine, et elle aura tendance à renvoyer un nouvel objet Integer et à copier à nouveau la valeur dans i ;
Nous connaissons donc la cause du problème. Parce qu'entre plusieurs threads, après i, i pointe vers un nouvel objet. Par conséquent, le thread peut charger une instance d'objet différente à chaque fois qu'il se verrouille. La solution est très simple, utilisez simplement l'une des trois méthodes de synchronisation ci-dessuspublic static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }Pour plus d'articles liés à l'utilisation du mot-clé java synchronisé, veuillez faire attention au site Web PHP chinois. !