"Programmation concurrente Java" problèmes de base courants

Processus et thread?

processus?

  • Un processus est un processus d'exécution d'un programme, l'unité de base du système pour exécuter un programme , et le processus est dynamique.
  • Le système exécutant un programme est le processus d'un processus depuis la création, l'opération jusqu'à la mort.

En Java, lorsque la fonction principale est démarrée, un processus JVM est en fait démarré et le thread où se trouve la fonction principale est le thread principal du processus.

Fil?

Un thread est un processus plus qu'une petite unité d'exécution.

Un processus génère plusieurs threads au cours de son exécution et plusieurs threads du même type partagent les ressources du tas et de la zone de méthode du processus . Chaque thread possède son propre compteur de programmes, sa pile de machines virtuelles et sa pile de méthodes locale.

Lorsque le système génère un thread ou que les commutateurs fonctionnent entre les threads, la charge est beaucoup plus faible que celle du processus , de sorte que le thread est également appelé processus léger.

Les programmes Java sont par nature des programmes multithreads, vous pouvez le vérifier via JMX:

public class Main {

    public static void main(String[] args) {
        // 获取 Java 线程管理 MXBean
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        // 不需要获取同步的 monitor 和 synchronizer 信息,仅获取线程和线程堆栈信息
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false,false);
        // 遍历线程信息,仅打印线程 ID 和线程名称信息 和 线程状态
        for (ThreadInfo threadInfo : threadInfos) {
            System.out.println("[" + threadInfo.getThreadId() + "]" + threadInfo.getThreadName() + "->" + threadInfo.getThreadState());
        }
    }
    
}

La sortie est la suivante:

[6]Monitor Ctrl-Break->RUNNABLE //这是IDEA特有的监控线程
[5]Attach Listener->RUNNABLE //添加事件
[4]Signal Dispatcher->RUNNABLE  // 分发处理给 JVM 信号的线程
[3]Finalizer->WAITING    //清除 reference 线程
[2]Reference Handler->WAITING  //清除 reference 线程
[1]main->RUNNABLE //main线程,程序入口

Un programme Java doit s'exécuter avec le thread principal et plusieurs threads en même temps.

Concurrence et parallélisme?

  • Concurrence:  plusieurs tâches sont exécutées à la même période (pas nécessairement exécutées en même temps par unité de temps);
  • Parallèle:  plusieurs tâches sont exécutées simultanément en unité de temps.

 

Parlez de la relation entre processus et thread du point de vue de JVM

Un processus peut avoir plusieurs threads et plusieurs threads partagent les ressources du tas et de la zone de méthode du processus [métaspace après JDK1.8], mais chaque thread a son propre compteur de programme indépendant, pile de machine virtuelle et pile de méthode locale .

Peut dessiner:

  • Un thread est une unité opérationnelle plus petite divisée en un processus. La plus grande différence entre un thread et un processus est que chaque processus est fondamentalement indépendant, mais chaque thread ne l'est pas nécessairement. Les threads d'un même processus sont susceptibles de s'influencer mutuellement.
  • La surcharge d'exécution des threads est faible, mais elle n'est pas propice à la gestion et à la protection des ressources, alors que le processus est exactement le contraire.

Pourquoi le compteur de programmes est-il privé?

Le rôle du compteur de programmes:

  • L'interpréteur de bytecode lit les instructions séquentiellement en changeant le compteur de programme, réalisant ainsi un contrôle de flux de code, tel que l'exécution séquentielle, la sélection, la mise en boucle et la gestion des exceptions.
  • Dans le cas du multithreading, le compteur de programme est utilisé pour enregistrer la position de l'exécution du thread en cours, de sorte que lorsque le thread est basculé en arrière, il est possible de savoir où le thread s'est exécuté la dernière fois .

Il est à noter que si la méthode native est exécutée, le compteur de programme enregistre l'adresse non définie. Ce n'est que lorsque le code Java est exécuté que le compteur de programme enregistre l'adresse de l'instruction suivante.

Résumé: Le compteur de programme privé est principalement utilisé pour restaurer la position d'exécution correcte après le changement de thread .

Pourquoi la pile de machines virtuelles et la pile de méthodes locales sont-elles privées?

  • Pile de machine virtuelle: lorsque  chaque méthode Java est exécutée, un cadre de pile est créé pour stocker la table de variables locales, la pile d'opérandes, la référence du pool de constantes et d'autres informations. Le processus de l'appel de méthode à la fin de l'exécution correspond au processus consistant à pousser et à sauter une trame de pile dans la pile de la machine virtuelle Java.
  • Pile de méthodes natives:  le rôle joué par la pile de machines virtuelles est très similaire. La différence est la suivante: la  pile de machines virtuelles sert à la machine virtuelle pour exécuter les méthodes Java (c'est-à-dire le bytecode), tandis que la pile de méthodes locale sert les méthodes natives utilisées par la machine virtuelle .  La machine virtuelle HotSpot et la pile de machines virtuelles Java sont combinées en une seule.

Résumé: La pile de la machine virtuelle et la pile de méthodes locales sont privées pour garantir que les variables locales du thread ne sont pas accessibles par d'autres threads .

Une phrase simple pour comprendre le tas et la zone de méthode

Le tas et la zone de méthode sont des ressources partagées par tous les threads. Le tas est la plus grande partie de mémoire du processus. Il est principalement utilisé pour stocker les objets nouvellement créés [la mémoire est allouée à presque tous les objets], et la zone de méthode est principalement utilisée pour stocker les objets chargés Données telles que les informations de classe, les constantes, les variables statiques et le code compilé par le compilateur juste à temps.

Pourquoi utiliser le multithreading? À quoi sert le multithreading?

Du général là- dessus:

  • Du fond de l'ordinateur: un thread peut être comparé à un processus léger, la plus petite unité d'exécution de programme, et le coût de commutation et de planification entre les threads est bien inférieur à celui d'un processus . De plus, l' ère des processeurs multicœurs signifie que plusieurs threads peuvent s'exécuter en même temps , ce qui réduit la surcharge du changement de contexte de thread.
  • Du point de vue des tendances contemporaines de développement d'Internet: le système actuel nécessite des millions, voire des dizaines de millions de simultanéité à chaque tour, et la programmation simultanée multithread est à la base du développement de systèmes à haute concurrence. L'utilisation de mécanismes multithreads peut grandement améliorer la concurrence globale du système. Capacité et performance .

Allez au bas de l'ordinateur pour discuter:

  • Ère monocœur: à l'ère du monocœur, le multithreading vise principalement à améliorer l'utilisation complète des processeurs et des périphériques IO. Lorsqu'il n'y a qu'un seul thread, le périphérique IO sera inactif lorsque le processeur calcule; lors de l'exécution d'opérations d'E / S, le processeur est inactif. On peut simplement dire que l'utilisation des deux est actuellement d'environ 50%. Mais quand il y a deux threads, c'est différent: quand un thread effectue des calculs CPU, l'autre thread peut effectuer des opérations d'E / S, de sorte que l'utilisation des deux peut atteindre 100% dans des conditions idéales.
  • Ère multi-core: le multi-threading à l'ère multi-core vise principalement à améliorer l'utilisation du processeur. Si nous voulons calculer une tâche complexe et que nous n'utilisons qu'un seul thread, un seul cœur de processeur du processeur sera utilisé, et la création de plusieurs threads permettra d'utiliser plusieurs cœurs de processeur, augmentant ainsi l'utilisation du processeur. .

Quels problèmes peuvent être causés par l'utilisation du multithreading?

  • Fuite de mémoire
  • Impasse
  • Fil non sécurisé

Affichage recommandé: Portal

Le cycle de vie et l'état du thread?

Six états

Explication de l'état du thread NOUVEAU L'état du thread qui n'a pas été démarré, c'est-à-dire que le thread est créé et que la méthode de démarrage n'a pas été appelée. RUNNABLE État prêt (début de l'appel, en attente de planification) + Exécution BLOQUÉ en attendant le verrouillage du moniteur , le thread dans l'état bloqué WAITING en attente en attend un autre Un thread effectue une opération spécifique (telle que notifier) ​​TIMED_WAITING un état d'attente avec un temps d'attente spécifié Le thread TERMINATED termine l'exécution, l' état de fin

Changement d'état

 

Remarque: la méthode join est une méthode sous la classe Thread. Elle est écrite comme Object dans la figure et doit être remplacée par Thread.join.

Une fois le thread créé, il sera dans l'état NEW (new) et il   commencera à s'exécuter après avoir appelé la méthode start (). Le thread est maintenant à l'état  READY (runnable)  . Le thread dans l'état exécutable est dans l'  état RUNNING après avoir obtenu la tranche de temps CPU (tranche de temps)  .

Lorsque le thread exécute la méthode wait (), le thread entre dans l'état  WAITING (en attente)  . Un thread qui entre dans l'état d'attente doit s'appuyer sur les notifications d'autres threads pour revenir à l'état en cours d'exécution, et l'   état TIME_WAITING équivaut à ajouter une limite de délai d'expiration à l'état d'attente, par exemple via la méthode sleep (long millis) ou wait (long millis) peut mettre le thread Java dans l'état TIMED WAITING. À l'expiration du délai, le thread Java revient à l'état RUNNABLE. Lorsque le thread appelle la méthode de synchronisation, le thread entre dans l'état BLOQUÉ (bloqué) sans acquérir le verrou   . Le thread entrera dans l'état TERMINATED (terminé) après avoir exécuté la méthode run () de Runnable   .

Qu'est-ce que le changement de contexte?

Dans la programmation multithread, le nombre de threads est généralement supérieur au nombre de cœurs de processeur, et un cœur de processeur ne peut être utilisé que par un thread à la fois . Afin de permettre à ces threads d'être exécutés efficacement, le processeur adopte une stratégie pour chaque thread. La forme d'allocation de tranches de temps et de rotation. Lorsque la tranche de temps d'un thread est épuisée, il sera à nouveau à l'état prêt pour que d'autres threads puissent l'utiliser. Ce processus est un changement de contexte.

En un mot: la tâche en cours enregistrera son état avant de passer à une autre tâche après l'exécution de la tranche de temps CPU, de sorte que l'état de cette tâche puisse être chargé à nouveau lors du retour à cette tâche la prochaine fois. Le processus de l'enregistrement au rechargement d'une tâche est un changement de contexte .

La commutation de contexte est généralement intensive en calcul . En d'autres termes, cela nécessite un temps processeur considérable: en dizaines ou centaines de commutations par seconde, chaque commutation prend des nanosecondes. Par conséquent, la commutation de contexte signifie consommer beaucoup de temps CPU pour le système, ce qui peut en fait être l'opération la plus chronophage du système d'exploitation.

Comparé à d'autres systèmes d'exploitation (y compris d'autres systèmes de type Unix), Linux présente de nombreux avantages, dont l'un est qu'il prend très peu de temps pour le changement de contexte et le changement de mode.

Qu'est-ce que le blocage de thread et comment éviter un blocage?

Plusieurs threads sont bloqués en même temps, l'un d'entre eux ou tous attendent qu'une ressource soit libérée.

Les amis qui ont étudié les systèmes d'exploitation savent que les quatre conditions suivantes doivent être remplies pour provoquer un blocage:

  1. Condition mutuellement exclusive : la ressource est occupée par un seul thread à la fois.
  2. Conditions de demande et de conservation : Lorsqu'un processus est bloqué par la demande de ressources, il conserve les ressources acquises.
  3. Conditions de non-privation : les ressources qu'un thread a acquises ne peuvent pas être privées de force par d'autres threads avant d'être épuisées, et les ressources ne sont libérées qu'après leur épuisement.
  4. Condition d'attente en circulation : une sorte de relation de ressource d'attente cyclique se forme entre plusieurs processus.

Comment éviter le blocage des threads?

J'ai évoqué les quatre conditions nécessaires à une impasse. Pour éviter une impasse, il suffit de détruire l'une des quatre conditions de l'impasse. Analysons maintenant un par un:

  1. Détruire la condition mutuellement exclusive  : nous n'avons aucun moyen de détruire cette condition, car nos utilisateurs voulaient à l'origine qu'elles s'excluent mutuellement (les ressources critiques nécessitent un accès exclusif).
  2. Demande de destruction et conditions de conservation  : s'appliquent à toutes les ressources à la fois.
  3. Conditions de destruction et de non-privation  : lorsqu'un thread occupant certaines ressources s'applique en outre à d'autres ressources, si l'application échoue, elle peut libérer activement les ressources qu'elle occupe.
  4. Détruisez les conditions d'attente du cycle  : évitez en sollicitant des ressources à la demande. Demandez des ressources dans un certain ordre et libérez les ressources dans l'ordre inverse. Détruisez la condition d'attente de boucle.

Parlez de la différence et des points communs entre les méthodes sleep () et wait ()?

  • Les deux principales différences sont que la méthode sleep ne libère pas le verrou, tandis que la méthode wait () libère le verrou.
  • Les deux peuvent suspendre l'exécution des threads.
  • Wait est généralement utilisé pour l'interaction / la communication entre les threads, et sleep est généralement utilisé pour suspendre l'exécution.
  • Après l'appel de la méthode wait (), le thread ne se réveillera pas automatiquement et les autres threads doivent appeler la méthode notify () ou notifyAll () sur le même objet. Une fois la méthode sleep () exécutée, le thread se réveillera automatiquement. Ou vous pouvez utiliser wait (long timeout) pour réveiller automatiquement le thread après l'expiration du délai.

Pourquoi exécutons-nous la méthode run () lorsque nous appelons la méthode start (), pourquoi ne pouvons-nous pas appeler la méthode run () directement?

C'est une autre question d'entrevue multi-thread java très classique, et elle est souvent posée pendant l'entretien. C'est simple, mais beaucoup de gens ne pourront pas répondre!

new a Thread, le thread entre dans un nouvel état; l'appel de la méthode start () démarre un thread et fait entrer le thread dans l'état prêt, et il peut commencer à s'exécuter lorsque la tranche de temps est allouée. start () effectuera le travail de préparation correspondant du thread, puis exécutera automatiquement le contenu de la méthode run (), qui est vraiment un travail multi-thread. En exécutant directement la méthode run (), la méthode run sera exécutée comme une méthode normale sous le thread principal, et elle ne sera pas exécutée dans un thread, donc ce n'est pas un travail multithread.

Résumé: Appelez la méthode start pour démarrer le thread et faire en sorte que le thread entre dans l'état prêt, tandis que la méthode run est juste un appel de méthode ordinaire de thread, ou elle est exécutée dans le thread principal.

Si vous pensez que cet article vous est utile, vous pouvez l'aimer et le suivre pour le soutenir, ou vous pouvez suivre mon compte public, il y a plus d'articles techniques sur les produits secs et le partage d'informations connexes, tout le monde peut apprendre et progresser ensemble!

 

Je suppose que tu aimes

Origine blog.csdn.net/weixin_50205273/article/details/108681994
conseillé
Classement