Avant Java 5, le mot-clé synchronisé a été utilisé pour implémenter la fonction de verrouillage.
Le mot-clé synchronisé peut être utilisé comme modificateur (méthode synchronisée) ou comme instruction dans une fonction (bloc de code synchronisé).
Pour maîtriser la synchronisation, la clé est de maîtriser l'utilisation de cette chose comme verrouillage. Pour les méthodes non statiques (méthodes membres) d'une classe, il signifie obtenir le verrou de l'instance de l'objet; Pour les méthodes statiques (méthodes de classe) d'une classe, il est nécessaire d'obtenir le verrou de l'objet de classe; Pour les blocs de code synchrones, il est nécessaire de spécifier le verrouillage de l'objet. Les méthodes non statiques synchronisées peuvent être considérées comme un bloc de code synchronisé (ceci) {…} contenant toute la méthode.
Qu'il s'agisse d'un bloc de code synchrone ou d'une méthode de synchronisation, un seul thread peut entrer à la fois (au maximum, un thread exécute le segment de code en même temps.), Et si d'autres threads essaient d'entrer (qu'il s'agisse du même bloc synchrone ou d'un bloc de synchronisation différent), JVM les accrochera (put dans le pool de verrouillage d'attente). Cette structure est appelée une section critique dans la théorie de la concurrence.
Dans JVM, afin d'améliorer l'efficacité, chaque thread exécutant en même temps aura une copie de cache des données qu'elle traite. Lorsque nous utilisons synchronzed pour la synchronisation, ce qui est vraiment synchronisé est le bloc de mémoire représentant l'objet verrouillé dans différents threads (les données de copie resteront synchronisées avec la mémoire principale. Nous savons maintenant pourquoi la synchronisation du mot est utilisée). Autrement dit, une fois le bloc de synchronisation ou la méthode de synchronisation exécutée, toutes les modifications apportées à l'objet verrouillé doivent être réécrites dans la mémoire principale avant de relâcher le verrou; Après être entré dans le bloc de synchronisation et obtenu le verrouillage, les données de l'objet verrouillé sont lues à partir de la mémoire principale et la copie de données du thread tenant le verrouillage doit être synchronisée avec la vue de données dans la mémoire principale.
Voici des exemples spécifiques pour illustrer les différentes situations de synchronisées.
Méthode de synchronisation synchronisée
Tout d'abord, jetons un coup d'œil à l'exemple de la méthode de synchronisation:
La classe publique SynchronizedTest1 étend Thread {private synchronisé void testSynchronizedMethod () {for (int i = 0; i <10; i ++) {System.out.println (Thread.currentThread (). GetName () + "TestSynchronizedMethod:" + i); essayez {thread.sleep (100); } catch (InterruptedException e) {e.printStackTrace (); }}} @Override public void run () {TestSynchronizedMethod (); } public static void main (String [] args) {synchroniséTest1 t = new SynchronizedTest1 (); t.start (); t.testSynchronizedMethod (); }}Exécution des sorties du programme:
TestSynchronisizedMethod: 0 TestSynchronizedMethod principal principal: 1 TestSynchronizedMethod: 2 TestSynchronizedMethod principal: 3 TestSynchronizedMethod principal: 4 TestSynchronizedMethod principaux TestSynchronizedMethod: 9 Thread-0 TestSynchronizedMethod: 0 Thread-0 TestSynchronizedMethod: 1 Thread-0 TestSynchronizedMethod: 2 Thread-0 TestSynchronizedMethod: 3 Thread-0 TestSynchronizedMethod: 4 Thread-0 TestSynchronizedMethod: 5 Thread-0 TestSyNChon TestSynchronizedMethod: 7 Thread-0 TestSynchronizedMethod: 8 Thread-0 TestSynchronizedMethod: 9
Vous pouvez voir que la méthode TestSynchronizedMethod est exécutée de manière synchrone entre deux threads.
Si la méthode principale est modifiée aux éléments suivants, les deux threads ne peuvent pas s'exécuter de manière synchrone, car le moniteur de synchronisation des deux threads n'est pas le même objet et ne peut pas jouer un rôle synchrone.
public static void main (String [] args) {thread t = new SynchronizedTest1 (); t.start (); Thread t1 = new SynchronizedTest1 (); t1.start (); }Le résultat de sortie est le suivant:
Thread-0 TestSynchronizedMethod: 0 Thread-1 TestSynchronizedMethod: 0 Thread-0 TestSynchronizedMethod: 1 Thread-1 TestSynchronizedMethod: 1 Thread-0 TestSynchronizedMethod: 2 Thread-1 TestSynchronizedMethod: 2 Thread-0 TestSynchronizedMethod: 3 Thread-1 TestSyNCHOD TestSynchronizedMethod: 4 Thread-1 TestSynchronizedMethod: 4 Thread-0 TestSynchronizedMethod: 5 Thread-1 TestSynchronizedMethod: 5 Thread-0 TestSynchronizedMethod: 6 Thread-1 TestSynchronizedMethod: 6 Thread-0 TestSynchronizedMethod: 7 Thread-1 TestSynchronizedMethod: 7 Thread-0 TestSynchronizedMethod: 8 Thread-1 TestSynchronizedMethod: 8 Thread-0 TestSynchronizedMethod: 9 Thread-1 TestSynchronizedMethod: 9
Si la méthode principale modifiée peut être exécutée de manière synchrone entre deux threads, la méthode TestSynchronizedMethod doit être déclarée comme une méthode statique, de sorte que les moniteurs des deux threads sont le même objet (objet de classe) et peuvent être exécutés de manière synchrone. Le code modifié ressemble à ceci:
classe publique SynchronizedTest1 étend Thread {private static synchronisé void testSynchronizedMethod () {for (int i = 0; i <10; i ++) {System.out.println (Thread.currentThread (). essayez {thread.sleep (100); } catch (InterruptedException e) {e.printStackTrace (); }}} @Override public void run () {TestSynchronizedMethod (); } public static void main (String [] args) {thread t = new synchronizedTest1 (); t.start (); Thread t1 = new SynchronizedTest1 (); t1.start (); }}Le résultat de sortie est le suivant:
Thread-0 TestSynchronizedMethod: 0 Thread-0 TestSynchronizedMethod: 1 Thread-0 TestSynchronizedMethod: 2 Thread-0 TestSynchronizedMethod: 3 Thread-0 TestSynchronizedMethod: 4 Thread-0 TestSynchronizedMethod: 5 Thread-0 TestSynchronizedMethod: 6 Thread-0 TestSynchronizedMethoD: 7 Thread-0 TestSynchronizedMethod: 8 Thread-0 TestSynchronizedMethod: 9 Thread-1 TestSynchronizedMethod: 0 Thread-1 TestSynchronizedMethod: 1 Thread-1 TestSynchronizedMethod: 2 Thread-1 TestSynchronizedMethod: 3 Thread-1 TestSynchronizedMethod: 4 Thread-1 TestSynchronizedmethod TestSynchronizedMethod: 7 Thread-1 TestSynchronizedMethod: 8 Thread-1 TestSynchronizedMethod: 9
La situation des blocs synchrones est similaire à la méthode de synchronisation, sauf que le bloc synchrone réduit la granularité du contrôle de synchronisation, qui peut mieux exercer l'efficacité de l'exécution parallèle multithread.
Utilisez cet objet pour contrôler la synchronisation entre les mêmes instances d'objet:
classe publique SynchronizedTest2 étend Thread {private void testSynchronizedBlock () {synchronisé (this) {for (int i = 0; i <10; i ++) {System.out.println (thread.currentThread (). GetName () + "testSynchronizedBlock:" + i); essayez {thread.sleep (100); } catch (InterruptedException e) {e.printStackTrace (); }}}} @Override public void run () {TestSynchronizedBlock (); } public static void main (String [] args) {synchroniséTest2 t = new SynchronizedTest2 (); t.start (); t.testSynchronizedBlock (); }}Résultat de sortie:
Main TestSynchronizedBlock: 0 Main TestSynchronizedBlock: 1 Main TestSynchronizedBlock: 2 Main TestSynchronizedBlock: 3 Main TestSynchronizedBlock: 4 Main TestSynchronizedBlock: 5 principaux TestSynchronizedBlock: 8 TestSynchronizedBlock principal: 9 TestSynchronizedBlock: 8 TestSynchronizedBlock: 9 Wile-0 TestSynchronizedBlock: 0 Thread-0 TestSynchronizedBlock: 1 Thread-0 TestSynchronizedBlock: 2 Thread-0 TestSynchronizedBlock: 3 Thread-0 TestSynchronizedBlock: 4 Thread-0 TestSynchronizedBlock: 5 Thread-0 TestSynchronizedBlock: 6 Thread-0 TestSynchronizedBlock: 7 Thread-0 TESTYNCHRONIFE: 8 Thread-0 TestSynchronizedBlock: 7 Thread-0 TESTYNCHRONIED: 8 Thread-0 TestSynchronizedBlock: 7 Thread-0 TESTYNCHRONIS TestSynchronizedBlock: 9
Utilisez des objets de classe pour contrôler la synchronisation entre différentes instances:
classe publique SynchronizedTest2 étend Thread {private void testSynchronizedBlock () {synchronisé (synchroniséTest2.class) {for (int i = 0; i <10; i ++) {system.out.println (thread.currentThread (). GetName () + "TestSynchroniséBlock:" + i); essayez {thread.sleep (100); } catch (InterruptedException e) {e.printStackTrace (); }}}} @Override public void run () {TestSynchronizedBlock (); } public static void main (string [] args) {thread t = new synchronizedTest2 (); t.start (); Thread t2 = new synchronizedTest2 (); t2.start (); }}Résultat de sortie:
Thread-0 TestSynchronizedBlock: 0 Thread-0 TestSynchronizedBlock: 1 Thread-0 TestSynchronizedBlock: 2 Thread-0 TestSynchronizedBlock: 3 Thread-0 TestSynChronizedBlock: 4 Thread-0 TestSynchronizedBlock: 5 Thread-0 TestSynchronizedBlock: 6 Thread-0 TestSynchronizedBlock Thread-0 TestSynchronizedBlock: 9 Thread-1 TestSynchronizedBlock: 0 Thread-1 TestSynchronizedBlock: 1 Thread-1 TestSynchronizedBlock: 2 Thread-1 TestSynchronizedBlock: 3 Thread-1 TestSynchronizedBlock: 6 Thread-1 TestSynchronizedBlock: 5 Thread-1. TestSynchronizedBlock: 8 Thread-1 TestSynchronizedBlock: 9
Lorsque vous utilisez le mot-clé synchronisé pour le contrôle de synchronisation, vous devez saisir le moniteur d'objet. Seul le processus qui obtient le moniteur peut fonctionner, et tout le reste doit être attendu pour obtenir le moniteur. Tout objet non nulle peut être utilisé comme moniteur d'objet. Lorsque la synchronisation agit sur une méthode, l'instance d'objet est verrouillée; Lorsque vous agissez sur une méthode statique, l'instance d'objet est verrouillée correspondant à l'objet.
Méthode synchrone pour deux threads pour accéder à un objet en même temps
Lorsque deux threads simultanés accèdent à la méthode synchrone du même objet, un seul thread peut être exécuté. Un autre thread doit attendre que le thread actuel l'exécute avant de pouvoir être exécuté.
classe publique twothread {public static void main (String [] args) {final twothread twothread = new twothread (); Thread t1 = nouveau thread (new Runnable () {public void run () {twothread.syncMethod ();}}, "a"); Thread t2 = new thread (new Runnable () {public void run () {twothread.syncMethod ();}}, "b"); t1.start (); t2.start (); } public synchronisé void syncMethod () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + ":" + i); essayez {thread.sleep (500); } catch (InterruptedException ie) {}}}}Résultat de sortie:
A: 0a: 1a: 2a: 3a: 4b: 0b: 1b: 2b: 3b: 4
La méthode de synchronisation de deux objets est accessible par deux threads
Dans ce cas, la synchronisation ne fonctionne pas, tout comme la méthode ordinaire. Parce que les verrous correspondants sont leurs objets respectifs.
classe publique TwoObject {public static void main (String [] args) {final TwoObject Object1 = new TwoObject (); Thread t1 = new Thread (new Runnable () {public void run () {object1.syncMethod ();}}, "object1"); t1.start (); final TwoObject Object2 = new TwoObject (); Thread t2 = new thread (new Runnable () {public void run () {public void run () {object2.SyncMethod ();}}, "object2"); t2.start (); } public synchronisé void syncMethod () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + ":" + i); essayez {thread.sleep (500); } catch (InterruptedException ie) {}}}}L'une des sorties possibles:
Objet2: 0Object1: 0Object1: 1Object2: 1Object2: 2Object1: 2Object2: 3Object1: 3Object1: 4Object2: 4
Les deux threads accèdent à la méthode statique synchronisée
Dans ce cas, comme la classe est verrouillée, à tout moment, un seul thread peut exécuter la méthode statique.
Accédant aux méthodes synchrones et aux méthodes asynchrones simultanément lorsqu'un thread accède à une méthode de synchronisation d'un objet, un autre thread peut toujours accéder aux méthodes asynchrones de cet objet.
classe publique SyncandNosync {public static void main (String [] args) {final syncandnosync syncandNosync = new SyncandNosync (); Thread t1 = new Thread (new Runnable () {public void run () {syncandnosync.SyncMethod ();}}, "a"); t1.start (); Thread t2 = nouveau thread (new Runnable () {public void run () {syncandnosync.nosyncMethod ();}}, "b"); t2.start (); } public synchronisé void syncMethod () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + "at syncMethod ():" + i); essayez {thread.sleep (500); } catch (InterruptedException ie) {}}} public void nosyncMethod () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + "at nosyncMethod ():" + i); essayez {thread.sleep (500); } catch (InterruptedException ie) {}}}}Une sortie possible:
B à NosyncMethod (): 0a à syncMethod (): 0b à NosyncMethod (): 1a à syncMethod (): 1b à NosyncMethod (): 2a à syncMethod (): 2b à NosyncMethod (): 3a à SyncMethod (): 3a at SyncMethod (): 4B à SyncThod (): 3a at SyncMethod (): 4b à SyncMethod (): 3a at SyncMethod (): 4b à SyncMethod () nosyncMethod (): 4
Différentes méthodes de synchronisation pour accéder au même objet
Lorsqu'un thread accède à la méthode de synchronisation A d'un objet, d'autres threads accès à toutes les autres méthodes de synchronisation de l'objet seront bloqués. Étant donné que le premier thread a obtenu le verrouillage de l'objet et que les autres threads ne peuvent pas obtenir le verrou, bien qu'il accéde à une méthode différente, il n'obtient pas le verrou et ne peut pas y accéder.
classe publique TwosyncMethod {public static void main (String [] args) {final twosyncMethod twosyncMethod = new TwosyncMethod (); Thread t1 = new Thread (new Runnable () {public void run () {twosyncMethod.SyncMethod1 ();}}, "a"); t1.start (); Thread t2 = new Thread (new Runnable () {public void run () {twosyncMethod.SyncMethod2 ();}}, "b"); t2.start (); } public synchronisé void syncMethod1 () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + "at syncMethod1 ():" + i); essayez {thread.sleep (500); } catch (InterruptedException ie) {}}} public synchronisé void syncMethod2 () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). GetName () + "à SyncMethod2 ():" + i); essayez {thread.sleep (500); } catch (InterruptedException ie) {}}}}Résultat de sortie:
A at syncMethod1(): 0A at syncMethod1(): 1A at syncMethod1(): 2A at syncMethod1(): 3A at syncMethod1(): 4B at syncMethod2(): 0B at syncMethod2(): 1B at syncMethod2(): 2B at syncMethod2(): 3B at syncMethod2 (): 4