Dans la programmation multithread, le problème le plus critique et le plus préoccupant devrait être la synchronisation. C'est un point difficile et aussi le cœur.
Des versions synchronisées et volatiles de jdk à l'interface Lock dans le package java.util.concurrent.locks fourni dans jdk 1.5 (implémenté avec ReadLock, WriteLock, ReentrantLock), l'implémentation du multi-threading mûrit également étape par étape. changement radical.
La synchronisation, par quel mécanisme est-elle contrôlée ? La première réaction est les verrous. Vous auriez dû y être exposé lors de l’apprentissage des systèmes d’exploitation et des bases de données. Dans un programme Java multithread, lorsque plusieurs programmes sont en compétition pour la même ressource, afin d'éviter toute corruption des ressources, un verrou d'objet est attribué au premier thread pour accéder à la ressource, et les suivants doivent attendre la libération du verrouillage d'objet.
Oui, la synchronisation des threads Java concerne principalement l'utilisation des ressources partagées.
Commençons par comprendre quelques ressources partagées des threads.
Comprenez à partir de la JVM quels threads partagent des données qui doivent être coordonnées :
Variables d'instance stockées dans le tas ; la zone méthode.
Lorsque la machine virtuelle Java charge une classe, chaque objet ou classe sera associé à un moniteur pour protéger les variables d'instance ou les variables de classe de l'objet bien sûr, si l'objet n'a pas de variables d'instance ou de classe, sans variables, le moniteur ne surveille rien.
Afin d'obtenir l'exclusivité mutuelle du moniteur mentionné ci-dessus, la machine virtuelle associe un verrou (également appelé verrou invisible) à chaque objet ou classe. Laissez-moi vous expliquer ici, les verrous de classe sont également obtenus via l'objet. locks. Ceci est obtenu car lorsqu'une classe est chargée, la JVM crée une instance de java.lang.Class pour chaque classe ; par conséquent, lorsque l'objet est verrouillé, l'objet de classe de cette classe est également verrouillé.
De plus, un thread peut verrouiller un objet plusieurs fois, ce qui correspond à plusieurs releases ; c'est un calculateur de verrou fourni par la JVM pour chaque verrou d'objet. Le dernier verrou est ajouté 1, le décrément correspondant est 1. , et lorsque la valeur de la calculatrice est 0, elle est libérée. Ce verrou d'objet est utilisé par le moniteur à l'intérieur de la JVM et est automatiquement généré par la JVM. Tous les programmeurs n'ont pas besoin de l'ajouter eux-mêmes.
Après avoir présenté le principe de synchronisation de Java, parlons d'abord de l'utilisation de synchronisé, et d'autres synchronisations seront introduites dans les chapitres suivants.
Essayons d'abord de donner un exemple.
package thread_test; /** * 测试扩展Thread类实现的多线程程序 * */ public class TestThread extends Thread{ private int threadnum; public TestThread(int threadnum) { this.threadnum = threadnum; } @Override public synchronized void run() { for(int i = 0;i<1000;i++){ System.out.println("NO." + threadnum + ":" + i ); } } public static void main(String[] args) throws Exception { for(int i=0; i<10; i++){ new TestThread(i).start(); Thread.sleep(1); } } }
Résultats d'exécution :
NO.0:887 NO.0:888 NO.0:889 NO.0:890 NO.0:891 NO.0:892 NO.0:893 NO.0:894 NO.7:122 NO.7:123 NO.7:124
Ce qui précède n'est qu'un extrait pour illustrer un problème.
Les enfants prudents constateront que NO.0:894 est suivi de NO.7:122, ce qui signifie qu'il ne commence pas de 0 à 999.
On dit que synchronisé peut implémenter des méthodes synchronisées ou des blocs synchronisés. Pourquoi cela ne peut-il pas être fait ici ?
Analysons d'abord le mécanisme de synchronisation. La synchronisation est réalisée via des verrous. Donc, dans l'exemple ci-dessus, quels objets ou classes sont verrouillés ? Il contient deux variables, l'une est i et l'autre est threadnum ; i est interne à la méthode et threadnum est privé.
Apprenons-en davantage sur le mécanisme de fonctionnement synchronisé :
Dans un programme Java, lors de l'utilisation d'un bloc synchronisé ou d'une méthode synchronisée, cette zone est marquée pour la surveillance lorsque la JVM traite le programme, lorsqu'un programme entre dans le programme ; zone de surveillance, l'objet ou la classe est automatiquement verrouillé.
Donc, dans l'exemple ci-dessus, une fois le mot-clé synchronisé utilisé, qu'est-ce qui est verrouillé ?
Lorsqu'une méthode synchronisée est utilisée, l'objet d'instance lui-même qui appelle la méthode est verrouillé en tant que verrou d'objet. Dans cet exemple, 10 threads ont leurs propres objets de classe TestThread créés par eux-mêmes, donc le verrou d'objet acquis est également leur propre verrou d'objet et n'a rien à voir avec les autres threads.
Pour implémenter le verrouillage de méthode, un objet partagé doit être verrouillé.
Modifiez l'exemple ci-dessus et jetez un autre regard :
package thread_test; /** * 测试扩展Thread类实现的多线程程序 * */ public class TestThread extends Thread{ private int threadnum; private String flag; //标记 public TestThread(int threadnum,String flag) { this.threadnum = threadnum; this.flag = flag; } @Override public void run() { synchronized(flag){ for(int i = 0;i<1000;i++){ System.out.println("NO." + threadnum + ":" + i ); } } } public static void main(String[] args) throws Exception { String flag = new String("flag"); for(int i=0; i<10; i++){ new TestThread(i,flag).start(); Thread.sleep(1); } } }
Un drapeau partagé est également ajouté. Synchronisez ensuite le drapeau via le bloc synchronisé ; cela remplit les conditions de verrouillage de l'objet partagé.
Oui, les résultats en cours sont arrivés dans l'ordre.
Grâce au bloc synchronisé, spécifiez l'acquisition du verrouillage de l'objet pour atteindre l'objectif de synchronisation. Existe-t-il une autre méthode pouvant être obtenue grâce à la méthode synchronisée ?
Selon le principe de synchronisation : Si vous pouvez obtenir un verrou d'objet partagé ou un verrou de classe, la synchronisation peut être réalisée. Alors pouvons-nous y parvenir en partageant un verrou de classe ?
Oui, nous pouvons utiliser des méthodes de synchronisation statiques. Selon les caractéristiques des méthodes statiques, elles ne peuvent être appelées que par l'objet de classe lui-même et ne peuvent pas être appelées en instanciant un objet de classe. Ensuite, si le verrou de cette méthode statique est obtenu, ce verrou de classe est obtenu, et ces verrous de classe sont tous des verrous de classe TestThread, et l'objectif d'obtenir le verrou de classe partagé est atteint.
Le code d'implémentation est le suivant :
package thread_test; /** * 测试扩展Thread类实现的多线程程序 * * @author ciding * @createTime Dec 7, 2011 9:37:25 AM * */ public class TestThread extends Thread{ private int threadnum; public TestThread(int threadnum) { this.threadnum = threadnum; } public static synchronized void staticTest(int threadnum) { for(int i = 0;i<1000;i++){ System.out.println("NO." + threadnum + ":" + i ); } } public static void main(String[] args) throws Exception { for(int i=0; i<10; i++){ new TestThread(i).start(); Thread.sleep(1); } } @Override public void run(){ staticTest(threadnum); } }
Le résultat d'exécution est le même que dans le deuxième exemple.
Le contenu ci-dessus explique principalement deux problèmes : le bloc de synchronisation et la méthode de synchronisation.
1. Bloc de synchronisation : Le verrou d'objet acquis est le verrou d'objet drapeau en synchronisé (drapeau).
2. Méthode synchrone : ce qui est obtenu est l'objet de classe auquel appartient la méthode et le verrou de l'objet de classe.
Méthode de synchronisation statique, puisque plusieurs threads la partageront, elle sera définitivement synchronisée.
Au lieu d'une méthode de synchronisation statique, elle ne sera synchronisée qu'en mode singleton.
Pour plus d'explications sur l'utilisation de base du mot-clé synchronisé dans la programmation multi-thread Java, veuillez faire attention au site Web PHP chinois !