Le titre est le suivant:
classe publique TestSync2 implémente Runnable {int b = 100; Synchronisé void M1 () lance InterruptedException {b = 1000; Thread.Sleep (500); // 6 System.out.println ("b =" + b); } synchronisé void m2 () lance InterruptedException {thread.sleep (250); // 5 b = 2000; } public static void main (String [] args) lève InterruptedException {testSync2 tt = new TestSync2 (); Thread t = nouveau thread (tt); // 1 t.start (); // 2 tt.m2 (); // 3 System.out.println ("Thread principal b =" + tt.b); // 4} @Override public void run () {try {m1 (); } catch (InterruptedException e) {e.printStackTrace (); }}}Le résultat de la sortie de ce programme?
Résultats de sortie du programme
Thread principal B = 2000b = 1000
ou
Thread principal B = 1000B = 1000
Vérifiez les points de connaissance
En Java, les programmes multi-thread sont les plus difficiles à comprendre et à déboguer. Plusieurs fois, les résultats d'exécution ne sont pas exécutés comme nous l'imaginons. Par conséquent, il est très difficile de multi-thread à Java. Je me souviens vaguement quand je prenais le niveau de langue C 2 au collège, quelle est la question dans ++ et de nombreux autres niveaux de priorité correspondent aux résultats de sortie finaux. Je veux répondre à des questions de priorité et de combinaison de coureur pour ce type de question. Récitez simplement en le soutenant, mais le multi-threading Java doit encore être bien compris, le dossier ne peut pas être fait.
Commençons une brève analyse:
Cette question implique 2 threads (Main Thread Main, Child Thread), et les mots clés impliquent une synchronisation et un thread.Sleep.
Le mot-clé synchronisé est encore assez compliqué (parfois je ne le comprends pas bien, donc la question ci-dessus a des malentendus). Sa fonction est de réaliser la synchronisation des threads (il existe de nombreuses méthodes pour implémenter la synchronisation des threads, c'est juste une sorte d'autres choses dont les articles de suivi parleront. Certaines implémentations du Master Doug Lea doivent être étudiées attentivement). Son travail consiste à verrouiller le code qui nécessite une synchronisation, afin qu'un seul thread puisse entrer le bloc de synchronisation à la fois (en fait, il s'agit d'une stratégie pessimiste) pour s'assurer que le fil ne se souvient que de la sécurité.
L'utilisation de mots clés généraux synchronisés
Dans le code ci-dessus, l'utilisation de synchronisée appartient en fait au deuxième cas. Agissant directement sur la méthode d'instance: il équivaut à verrouiller l'instance actuelle. Avant d'entrer dans le code de synchronisation, vous devez obtenir le verrou de l'instance actuelle.
Malentendus possibles
1. Étant donné que nous ne comprenons pas plusieurs fois synchronisé, nous exploitons une méthode synchronisée par multi-threading. Lorsque deux threads appellent 2 méthodes synchronisées différentes, on pense qu'elle n'est pas pertinente. Cette idée est un malentendu. Agissant directement sur la méthode d'instance: il équivaut à verrouiller l'instance actuelle. Avant d'entrer dans le code de synchronisation, vous devez obtenir le verrou de l'instance actuelle.
2. Si une méthode synchronisée est appelée. Un autre appel à une méthode normale n'a rien à faire, et les deux n'ont pas de relation d'attente.
Ceux-ci sont très utiles pour l'analyse ultérieure.
Thread.sleep
Safess le thread actuel (c'est-à-dire le thread qui appelle la méthode) pendant une période de temps, donnant à d'autres threads une chance de continuer à exécuter, mais il ne libére pas le verrouillage de l'objet. C'est-à-dire que si la synchronisation synchronisée est rapide, d'autres threads ne peuvent toujours pas accéder aux données partagées. Notez que cette méthode doit prendre des exceptions, ce qui est très utile pour une analyse ultérieure.
Processus d'analyse
Java est exécuté à partir de la méthode principale. On dit qu'il y a 2 threads, mais même si vous modifiez la priorité du thread ici, il est inutile. La priorité n'est que lorsque les deux programmes n'ont pas encore été exécutés. Maintenant, une fois ce code exécuté, le thread principal a été exécuté. Pour la variable d'attribut int b = 100, il n'y aura pas de problème de visibilité car synchronisé est utilisé (et il n'est pas nécessaire de dire que la déclaration volatile est utilisée), lors de l'exécution de l'étape 1 (thread t = nouveau thread (TT); // 1), le thread est à nouveau état et n'a pas encore commencé à fonctionner. Lorsque les 2 étapes sont exécutées (t.start (); // 2) lorsque la méthode de démarrage est appelée, le thread est réellement démarré et entre dans l'état de course. L'état exécutable signifie qu'il peut être exécuté et que tout est prêt, mais cela ne signifie pas qu'il doit être exécuté sur le CPU. La question de savoir s'il y a une réelle exécution dépend de la planification du CPU de service. Ici, lors de l'exécution des 3 étapes, vous devez d'abord obtenir le verrou (car Start doit appeler la méthode native, et tout est prêt une fois l'utilisation terminée, mais cela ne signifie pas qu'il doit être exécuté sur le CPU. Si la méthode d'exécution sera appelée et la méthode M1 sera exécutée). En fait, peu importe que le thread.sheep dans les deux méthodes synchronisées soit, cela ajoute probablement des difficultés à la confusion. Lorsque les 3 étapes sont exécutées, le thread de l'enfant est réellement prêt bientôt, mais parce que Synchronisé existe et agit comme le même objet, le fil d'enfant doit attendre. Étant donné que l'ordre d'exécution dans la méthode principale est exécuté séquentiellement, elle doit être terminée après l'exécution de l'étape 3 avant que la 4e étape ne puisse être atteinte. Étant donné que l'exécution de l'étape 3 est terminée, le thread enfant peut exécuter M1. Il y a un problème ici qui obtient le multi-thread en premier. Si les 4 étapes sont obtenues d'abord, alors le thread principal B = 2000. Si le thread de l'enfant M1 est obtenu, B peut avoir été attribué à 1000 ou avant que les 4 étapes ne soient attribuées, le résultat possible est le thread principal B = 1000 ou le thread principal B = 2000. Si les 6 étapes sont supprimées ici, alors b = exécuter avant et le thread principal b = avant est incertain. Cependant, comme 6 étapes existent, quoi qu'il arrive, le thread principal B = est devant, cela dépend donc de la situation s'il est égal à 1000 ou 2000. Après cela, B = 1000 est définitivement fixé.
Quelques suggestions pour le multi-threading
Il y a aussi quelques conseils à partager dans les articles suivants. Le multi-lancement est particulièrement important et difficile. J'espère que tout le monde y passera plus de temps.
Quelques techniques de débogage pour le multithreading
En raison des points d'arrêt, tous les fils doivent s'arrêter lors du passage des points d'arrêt, ce qui fait que ce point est constamment interrompu, ce qui est très inconfortable. Il y a des points d'arrêt conditionnels dans ECLISPE, et vous pouvez vous arrêter lorsque les conditions sont remplies, donc c'est pratique.
Résumer
Ce qui précède est la question d'interview Java la plus difficile de l'histoire que l'éditeur vous a présentée. J'espère que cela vous sera utile. Si vous avez des questions, veuillez me laisser un message et l'éditeur vous répondra à temps. Merci beaucoup pour votre soutien au site Web Wulin.com!