Maison >Java >javaDidacticiel >Jetons un coup d'œil aux 4 fonctionnalités de synchronisé
1. Réentrée du verrouillage synchronisé
1.1 Introduction
Le mot-clé synchronisé a la fonction de réentrée du verrou, c'est-à-dire que lors de l'utilisation de synchronisé, lorsqu'un thread obtient un verrou d'objet, il peut obtenir à nouveau le verrou d'objet lorsqu'il demande à nouveau le verrou d'objet. Cela montre que lors de l'appel d'autres méthodes/blocs synchronisés de cette classe au sein d'une méthode/bloc synchronisé, le verrou peut toujours être obtenu.
Par exemple :
public class Service1 { public synchronized void method1(){ System.out.println("method1"); method2(); } public synchronized void method2(){ System.out.println("method2"); method3(); } public synchronized void method3(){ System.out.println("method3"); } }rrree
Le résultat en cours d'exécution est le suivant :
☹ Quand j'ai vu ce résultat, j'étais confus, pourquoi ? Est-il prouvé que la serrure est réentrante ?
➤ Le concept de « verrouillage réentrant » est que vous pouvez à nouveau acquérir votre propre verrou interne. Par exemple, un thread acquiert le verrou d'un objet à ce moment-là, lorsqu'il le souhaite. l'acquérir à nouveau, il est toujours possible d'acquérir le verrou de cet objet. Si le verrou ne peut pas être réentrant, un blocage se produira.
➤ Le plus grand rôle du "verrouillage réentrant" est d'éviter les impasses
Analyse 1.2
Nous en tant que vous savez, dans le programme, le verrou sur le moniteur de synchronisation ne peut pas être explicitement libéré, mais le verrou sera libéré dans les situations suivantes :
① Libéré à la fin de l'exécution de la méthode de synchronisation et du bloc de code du thread actuel
② Libéré lorsque le thread actuel rencontre un break ou un return pour terminer le bloc de code ou la méthode dans une méthode synchronisée ou un bloc de code synchronisé
③ Libéré lorsqu'une erreur ou une exception non gérée se produit provoquant une fin anormale
④ Le programme exécute la synchronisation méthode d'attente d'objet, le thread actuel fait une pause et libère le verrou
Ensuite, dans le programme ci-dessus, lorsque le thread entre dans la méthode de synchronisation méthode1, il obtient le verrou d'objet de Service1, mais lors de l'exécution de méthode1, la méthode de synchronisation méthode2 est appelée selon la méthode normale. Dans ce cas, lors de l'exécution de la méthode de synchronisation méthode2, vous devez également obtenir le verrouillage de l'objet. Cependant, selon les conditions de déverrouillage ci-dessus, le verrouillage de l'objet de la méthode 1 n'a pas été libéré pour le moment. , ce qui entraînera un blocage et la méthode2 ne pourra pas continuer à être exécutée. Cependant, à en juger par les résultats d'exécution du code ci-dessus, méthode2 et méthode3 peuvent être exécutées normalement, ce qui signifie que lors de l'appel d'autres méthodes synchronisées ou blocs de code de cette classe dans une méthode ou un bloc de code synchronisé, vous pouvez toujours obtenir la serrure .
1.3 Héritage parent-enfant
Les verrous réentrants sont pris en charge dans l'environnement d'héritage de classe parent-enfant. L'exemple de code est le suivant :
public class MyThread extends Thread { @Override public void run(){ Service1 service1 = new Service1(); service1.method1(); } public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); } }
public class Service2 { public int i = 10; public synchronized void mainMethod(){ i--; System.out.println("main print i="+i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class Service3 extends Service2 { public synchronized void subMethod(){ try{ while (i>0){ i--; System.out.println("sub print i= "+i); Thread.sleep(100); this.mainMethod(); } }catch (InterruptedException e){ e.printStackTrace(); } } }
Les résultats d'exécution sont les suivants :
Ce programme illustre que lorsqu'il existe une relation d'héritage de classe parent-enfant, la classe enfant peut complètement appeler la méthode de synchronisation du parent classe via un "verrouillage réentrant".
2. Lorsqu'une exception se produit, le verrou est automatiquement libéré
Lorsqu'une exception se produit dans le code exécuté par un thread, le verrou il détient est automatiquement libéré.
Le code de vérification est le suivant :
public class MyThread extends Thread { @Override public void run(){ Service3 service3 = new Service3(); service3.subMethod(); } public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); } }
public class Service4 { public synchronized void testMethod(){ if(Thread.currentThread().getName().equals("a")){ System.out.println("ThreadName= "+Thread.currentThread().getName()+" run beginTime="+System.currentTimeMillis()); int i=1; while (i == 1){ if((""+Math.random()).substring(0,8).equals("0.123456")){ System.out.println("ThreadName= "+Thread.currentThread().getName()+" run exceptionTime="+System.currentTimeMillis()); //Integer.parseInt("a"); } } }else{ System.out.println("Thread B run time= "+System.currentTimeMillis()); } } }
public class ThreadA extends Thread{ private Service4 service4; public ThreadA(Service4 service4){ this.service4 = service4; } @Override public void run(){ service4.testMethod(); } }
public class ThreadB extends Thread{ private Service4 service4; public ThreadB(Service4 service4){ this.service4 = service4; } @Override public void run(){ service4.testMethod(); } }
Notez que dans la classe Service4, Integer.parseInt("a"); est commenté à ce moment, et les résultats en cours d'exécution sont comme suit :
Puisque le thread a n'a pas d'erreur, alors que (true), le thread a est dans une boucle infinie à ce moment, le verrou est toujours occupé par a, le thread b ne peut pas obtenir le verrou, c'est-à-dire que le thread b ne peut pas être exécuté.
Décommentez le Integer.parseInt(“a”); dans la classe Service4, et le résultat de l'exécution est le suivant :
Lorsqu'un Une erreur se produit dans le thread a, le thread b obtient le verrou et l'exécute. On peut voir que lorsqu'une exception se produit dans la méthode, le verrou est automatiquement libéré.
3. Utilisez n'importe quel objet comme moniteur
Java prend en charge "n'importe quel objet" comme "moniteur d'objet" pour implémenter la synchronisation. La plupart de ces "objets arbitraires" sont des variables d'instance et des paramètres de méthode, et le format est un bloc de code synchronisé (pas cet objet x).L'exemple de code est le suivant :
public class Main { public static void main(String[] args) { try { Service4 service4 = new Service4(); ThreadA a = new ThreadA(service4); a.setName("a"); a.start(); Thread.sleep(500); ThreadB b = new ThreadB(service4); b.setName("b"); b.start(); } catch (InterruptedException e) { e.printStackTrace(); } } }Les résultats d'exécution sont les suivants :
Le verrouillage d'objets non-this présente certains avantages : s'il existe de nombreuses méthodes synchronisées dans un classe, bien que la synchronisation puisse être réalisée, mais elle sera bloquée, donc l'efficacité de fonctionnement est affectée ; mais si un bloc de code synchronisé est utilisé pour verrouiller un objet non-this, le programme et la méthode de synchronisation dans le code synchronisé (non-this) ; Le bloc est asynchrone et d'autres méthodes de synchronisation de verrouillage ne sont pas autorisées. La lutte pour ce verrouillage peut grandement améliorer l'efficacité opérationnelle.
4. La synchronisation n'a pas d'héritage
La méthode de synchronisation de la classe parent ne fonctionnera pas si elle est réécrite dans la sous-classe sans ajouter le mot-clé de synchronisation. synchrone, vous devez donc ajouter le mot-clé synchronisé à la méthode de la sous-classe.
Apprentissage recommandé : Tutoriel vidéo Java
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!