Mot-clé synchronisé
La touche synchronisée peut modifier les fonctions et les instructions dans les fonctions. Qu'il soit ajouté aux méthodes ou objets, le verrouillage qu'il acquiert est un objet, plutôt que de traiter un morceau de code ou une fonction comme un verrou.
1. Lorsque deux threads simultanés accèdent au bloc de code synchronisé synchronisé (this) dans le même objet, un seul thread peut être exécuté pendant une période de temps, et l'autre thread ne peut exécuter ce code actuel que le thread actuel a terminé l'exécution.
2. Lorsqu'un thread accède à un bloc de code synchronisé (ce) synchronisé dans un objet, d'autres threads peuvent toujours accéder à d'autres blocs de code non synchronisés (ce) dans cet objet.
3. Il convient de noter ici que lorsqu'un thread accède à un bloc de code synchronisé (ce) objet, d'autres threads seront empêchés d'accéder à d'autres blocs de code synchronisés synchronisés (this) dans cet objet.
4. Ce qui précède est également applicable à d'autres blocs de code de synchronisation, c'est-à-dire lorsqu'un thread accède à un bloc de code de synchronisation synchronisé (cette) de synchronisation d'un objet, le thread obtient le verrouillage de l'objet de l'objet. De plus, chaque objet (c'est-à-dire l'instance de classe) correspond à un verrou. Il appartient est bloqué. Ce mécanisme garantit qu'en même temps, pour chaque objet, tout au plus une de toutes les fonctions membres déclarées synchronisées se trouvent dans un état exécutable (car tout au plus un thread peut acquérir le verrou de l'objet), évitant ainsi l'accès aux variables des membres de la classe .
Inconvénients de la méthode synchronisée:
Puisque synchronisé verrouille l'objet qui appelle cette méthode de synchronisation, c'est-à-dire lorsqu'un thread P1 exécute cette méthode dans différents threads, ils formeront des exclusions mutuelles, réalisant ainsi l'effet de la synchronisation. Mais il convient de noter ici qu'un autre objet de classe qui est de cet objet peut appeler arbitrairement cette méthode avec le mot-clé synchronisé ajouté. L'essence de la méthode de synchronisation est de s'appliquer à la référence d'objet. Ce cas. Nous expliquerons cette situation en détail ci-dessous:
Tout d'abord, introduire deux objets verrouillés avec le mot clé synchronisé: objet et classe - Synchronisé peut ajouter des verrous d'objets ou des verrous de classe aux ressources. De cette classe, d'autres objets de cette classe peuvent toujours utiliser la méthode synchronisée qui a verrouillé l'objet précédent.
L'un des principaux problèmes que nous discutons ici est: "La même classe et les différentes instances appellent-elles la même méthode, y aura-t-elle un problème de synchronisation?"
Le problème de synchronisation est uniquement lié à la ressource, et cela dépend si la ressource est statique. Pour les mêmes données statiques, votre même fonction appartient à différents threads pour les lire et l'écrire en même temps, et le CPU ne générera pas d'erreurs. Vous. Même si vous avez deux codes différents fonctionnant dans deux cœurs différents du CPU et écrivant une adresse mémoire en même temps, le mécanisme de cache en verra un en L2 en premier. Ensuite, mettez à jour et partagez-le avec un autre noyau, et il n'y aura pas d'erreurs, sinon Intel ou AMD seront en vain.
Par conséquent, tant que vous n'avez pas la même ressource ou la même variable que deux codes, il n'y aura pas d'incohérence des données. De plus, les appels vers différents objets de la même classe ont des piles complètement différentes, et elles sont complètement hors de propos.
Ici, nous utilisons un exemple pour illustrer le processus de vente de billets, où nos ressources partagées sont le nombre restant de billets.
package com.test; public class threadsaFeTest étend le thread Runnable {private static int num = 1; VOIDE (Nom de la chaîne) {if (num> 0) {Système. (Terminé en environ 5 secondes). ; (Système: Nombre actuel de votes: "+ num); Args []) {Try {New ThreadSaFeTest ("Vendeur de billets LI XX") .Start (); {e.printStackTrace (); Exécutez le code ci-dessus et la sortie que nous obtenons est:
Conducteur de billets Li XX: Le nombre de billets de test est supérieur à 0 Conducteur de billets Li XX: Le paiement est collecté (terminé en environ 5 secondes). . . Vendeur de billets King X: Le nombre de billets de test est supérieur à 0 vendeur de billets King X: Le paiement est collecté (terminé en environ 5 secondes). . . Vendeur de billets Li XX: Imprimez la facture, système d'achèvement des ventes de billets: Nombre actuel de votes: 0 Vendeur de billets Wang X: Imprimez la facture, Système d'achèvement des ventes de billets: Nombre actuel de votes: -1 AVERTISSEMENT: Le nombre de votes est inférieur à celui de 0, les nombres négatifs apparaissent
Sur la base des résultats de sortie, nous pouvons constater que les votes restants sont -1 et il y a un problème d'erreur de synchronisation. La raison en est que les deux objets d'instance que nous avons créés ont modifié la ressource statique partagée static int num = 1 en même temps. Ensuite, nous supprimons le modificateur statique dans la case dans le code ci-dessus, puis exécutons le programme pour obtenir:
Conducteur de billets Li XX: Le nombre de billets de test est supérieur à 0 Conducteur de billets Li XX: Le paiement est collecté (terminé en environ 5 secondes). . . Vendeur de billets King X: Le nombre de billets de test est supérieur à 0 vendeur de billets King X: Le paiement est collecté (terminé en environ 5 secondes). . . Vendeur de billets Li XX: Imprimez la facture, système d'achèvement des ventes de billets: Nombre actuel de billets: 0 Vendeur de billets Wang X: Imprimez la facture, Système d'achèvement des ventes de billets: Nombre actuel de billets: 0
Après avoir modifié le diplôme, le programme s'exécute sans aucun problème.但这样却违背了我们希望多线程同时对共享资源的处理(去static后,num就从共享资源变成了每个实例各自拥有的成员变量),这显然不是我们想要的。
Dans les deux codes ci-dessus, l'essentiel à adopter est de verrouiller l'objet.由于我之前谈到的原因,当一个类的两个不同的实例对同一共享资源进行修改时,CPU为了保证程序的逻辑会默认这种做法,至于是不是想要的结果,这个只能由程序员自己来决定。 Therefore, we need to change the scope of the lock. If the target is just an instance, then this problem is inevitable. Only when the scope of the lock is the entire class, can different instances of the same class be excluded from sharing resources en même temps.
package com.test;public class ThreadSafeTest extends Thread implements Runnable { private static int num = 1; public ThreadSafeTest(String name) { setNa me(name); } public void run() { sell(getName()); } private synchronized STATIC VOID Sell (Nom de la chaîne) {if (num> 0) {Système. Collecte de paiement (environ 5 secondes terminées). ();} Catch (InterruptedException e) {e.PrintStackTrace ();}} else {System. .println ("Système: le nombre de votes actuel:" + num); String Args []) {Try {New ThreadSaFeTest ("Ticket Vender Li XX") .Start (); ) {e.printStackTrace ();}}} Faites le programme comme ci-dessus pour obtenir le résultat de l'exécution:
Conducteur de billets Li XX: Le nombre de billets de test est supérieur à 0 Conducteur de billets Li XX: Le paiement est collecté (terminé en environ 5 secondes). . . Vendeur de billets Li XX: Imprimez le ticket, le système d'achèvement des ventes de billets: Nombre actuel de billets: 0 Vendeur de billets Wang X: Pas de billets, Arrêtez les ventes de billets
Un modificateur statique est ajouté à la méthode Sell (), de sorte que l'objet de verrouillage devient une classe. Cela obtiendra les résultats que nous voulons comme prévu.
Résumer:
1. Il existe deux utilisations de mots clés synchronisés: méthode synchronisée et bloc synchronisé.
2. Dans Java, ce n'est pas seulement une instance de classe, mais chaque classe peut également correspondre à un verrou.
1. Le mot-clé synchronisé ne peut pas être hérité. Bien que synchronisé puisse être utilisé pour définir des méthodes, Synchronisé n'appartient pas à une partie de la définition de la méthode, de sorte que le mot clé synchronisé ne peut pas être hérité. Si une méthode dans la classe parent utilise le mot-clé synchronisé et que la sous-classe remplace également cette méthode, par défaut, cette méthode dans la sous-classe n'est pas synchronisée, et elle doit être affichée pour ajouter la méthode dans la sous-classe. . Bien sûr, vous pouvez également appeler les méthodes correspondantes de la classe parentale dans la sous-classe. être synchronisé. comme,
Ajoutez un mot-clé synchronisé à la sous-classe:
classe Parent {public synchronisé void méthode () {}} classe l'enfant étend le parent {public synchronisé void méthode () {}} Appelez la méthode de la classe parent:
Classe Parent {public Synchronized Void Method () {}} La classe Child étend Parent {public void Method () {super.Method (); 2. Le mot-clé synchronisé ne peut pas être utilisé lors de la définition de la méthode d'interface.
3. Le constructeur ne peut pas utiliser le mot-clé synchronisé, mais le bloc synchronisé peut être utilisé pour la synchronisation.
4. La position synchronisée peut être placée librement, mais ne peut pas être placée derrière le type de retour de la méthode.
5. Le mot-clé synchronisé ne peut pas être utilisé pour synchroniser les variables, telles que le code suivant est incorrect:
Public synchronisé int n = 0; public statique publique int n = 0;
6. Bien que l'utilisation du mot-clé synchronisé soit la méthode de synchronisation la plus sûre, si elle est utilisée en grande quantité, elle entraînera également une consommation de ressources inutile et des pertes de performances. À la surface, la synchronisation verrouille une méthode, mais en fait, il verrouille une classe. exécuté. Les méthodes statiques sont similaires aux méthodes non statiques. Cependant, les méthodes statiques et les méthodes non statiques ne se affecteront pas mutuellement, voir le code suivant:
classe publique MyThread1 étend le thread {Public String MethodName; ");} public synchronisé void méthode2 () {méthode (" méthode non statique2 "méthode");} public static synchronisé void méthode3 () {méthode ("méthode statique3" méthode ");} public statique synchronisé vide méthode4 () {méthode (Méthode statique4 ");} public void run () {try {getClass (). getMethod (méthode) .invoke (this); ) lance l'exception {mythread1 mythread1 = new MyThread1 (); ); Le résultat en cours est:
Méthode non statique1 Méthode Méthode statique3 Méthode
À partir des résultats d'exécution ci-dessus, nous pouvons voir que la méthode2 et la méthode 4 ne s'exécuteront pas tant que la méthode1 et la méthode3 ne seront pas terminées. Par conséquent, nous pouvons tirer une conclusion que si nous utilisons la synchronisation synchronisée pour définir des méthodes non statiques dans la classe, cela affectera toutes les méthodes non statiques définies synchronisées dans cette classe; Dans cette classe, méthode statique définie par synchronisée. C'est un peu comme un verrouillage de table dans une table de données. Par conséquent, l'utilisation intensive de cette méthode de synchronisation réduira considérablement les performances du programme.
Conseils pour un accès plus sûr aux ressources partagées:
1. Définissez la variable d'instance de privé + sa méthode GET, au lieu de définir la variable d'instance de public / protégé. Si une variable est définie comme publique, l'objet peut l'obtenir directement en contournant le contrôle de la méthode de synchronisation dans le monde extérieur et le modifier. Il s'agit également de l'une des implémentations standard de Javabean.
2. If the instance variable is an object, such as an array or an ArrayList, then the above method is still unsafe, because when the outside world gets the reference to the instance object through the get method and points it to another object, then La variable privée est également changée, ne serait-ce pas très dangereuse? À l'heure actuelle, vous devez ajouter la méthode synchronisée pour obtenir une méthode et retourner clone () de cet objet privé. De cette façon, ce que l'appelant obtient, c'est juste une référence à la copie d'objet.
Trois façons d'obtenir un moniteur d'objet (verrouillage) et notify ()
Dans une méthode de thread, les appels à attendre () et notify () doivent spécifier un objet d'objet et le thread doit avoir le moniteur de l'objet objet. Le moyen le plus simple d'obtenir le moniteur d'objet est d'utiliser le mot clé synchronisé sur l'objet. Après avoir appelé la méthode Wait (), le thread libèrera le verrouillage de l'objet et entrera dans l'état de sommeil. Lorsque d'autres threads appellent la méthode Notify (), le même objet doit être utilisé.
Pour plusieurs méthodes verrouillées par un objet, l'une d'elles sera sélectionnée pour se réveiller lors de l'appel de la méthode Notify (), et Notifyall () réveillera tous ses threads en attente.
package net.mindView.util; import javax.swing.jframe; public class waitandNotify {public static void main (String [] args) {System. frame. 300, 100); {t = new waitandNotifythread (waitandNotifyjframe.tout); t.start (); .add (start); jbutton pause = new Jbutton (new AbstractAction ("Pause") {public void ActionPerformed (ActionEvent e) {if (t! = null) {t. Iswait = true;}}}); (pause); jbutton end = new JButton (new AbstractAction ("end") {public void ActionPerformed (ActionEvent e) {if (t! = null) {t.interrupt (); t = null;}}}); .Add (end); = f; Iswait = false; if (isWait) wait ();}}} catch (exception e) {}} public void n () {synchronisé (this) {notify (); Comme dans le code dans l'exemple ci-dessus, si le bloc de code synchrone est supprimé, l'exécution lancera une exception Java.lang.ILLEGALMONITERSTATEException.
En regardant le JDK, nous pouvons voir que la raison de cette exception est que le thread actuel n'est pas le propriétaire de ce moniteur d'objet.
Cette méthode ne doit être appelée que par un fil qui est le propriétaire de ce moniteur d'objet.
1. En exécutant la méthode d'instance synchrone de cet objet, comme:
public synchronisé void n () {notify (); 2. En exécutant le corps de l'instruction synchronisée qui est synchronisée sur cet objet, comme:
public void n () {synchronisé (this) {notify (); 3. Pour les objets de type de classe, vous pouvez exécuter des méthodes statiques synchrones de cette classe.
Lorsque nous appelons une méthode statique, nous ne créons pas nécessairement un objet d'instance. Therefore, this cannot be used to synchronize static methods, so the Class object must be used to synchronize static methods. Since the notify() method is not a static method, we cannot set the n() method to be static, so we use Un autre exemple pour illustrer:
public class SynchronizedStatic implements Runnable { private static boolean flag = true;//Class object synchronization method one: // Pay attention to the synchronization method of static modification, monitor: SynchronizedStatic.class pri vate static synchronized void testSyncMethod() { for (int i = 0; i <100; // Méthode de synchronisation des objets de classe 2: private void TestSyncBlock () {// Affichage utilise la classe Get comme moniteur. C'est la même chose que la méthode synchronisée statique obtient implicitement le moniteur de classe. Synchronisé (synchronisé. ("TestSyncBlock:" + i);}}} public void run () {// Flag est une variable de statique. Par conséquent, différents threads exécuteront différentes méthodes, et ce n'est que de cette manière que vous pouvez voir différents effets de verrouillage. if (Flag) {Flag = false; = Nouveau synchronisé (); Le code ci-dessus exécute le résultat du fonctionnement de deux méthodes de synchronisation pour imprimer 100 nombres de 0 à 99 en même temps. Block. Les deux méthodes sont similaires. Étant donné que la portée de la méthode un et la méthode sont les deux classes, elles sont mutuellement exclusives. Par conséquent, le résultat en cours d'exécution sera:
TestSyncMethod: 0TESTSYNCMETHOD: 1 ... ... TestSyncMethod: 99tesTyncBlock: 0 ... ... TestSyncBlock: 99
Cependant, si nous remplaçons SynchronizedStatic.
TestSyncBlock: 0TESTSYNCMETHOD: 0TESTSYNCBLOCK: 1TESTSYNCMETHOD: 1 ... ... TestSyncMethod: 99tesTyncBlock: 99
Il y a deux portées de verrous, l'une est l'objet de la classe et l'autre est la classe elle-même. Dans le code ci-dessus, deux méthodes sont données pour faire de la portée du verrouillage une classe, afin que la synchronisation puisse être complétée entre différents objets de la même classe.
Pour résumer ce qui précède, les points suivants doivent être notés:
1. Wait (), notify (), et notifyall () doivent tous être exécutés sous la prémisse d'avoir le moniteur d'objet, sinon un java.lang.LelegalMonitorStateException sera lancé.
2. Plusieurs threads peuvent attendre sur un objet en même temps.
3. Notify () est de réveiller au hasard un fil en attente de l'objet.
4. Le thread qui est éveillé par Notify () ne se réveille pas immédiatement après l'exécution de Notify (), mais seulement après que le thread Notify () a libéré le moniteur d'objet.
5. Ces méthodes d'objet sont encore loin des méthodes de sommeil et d'interruption de Thread, alors ne les confondez pas.