Les didacticiels Java indiquent que la création d'un thread coûte cher. Mais pourquoi est-ce cher exactement? Que se passe-t-il exactement lors de la création d'un thread Java qui rend sa création coûteuse? Je considère cette affirmation comme vraie, mais je m'intéresse simplement aux mécanismes de création de threads dans JVM.
Frais généraux du cycle de vie des threads. La création et le démontage de fils ne sont pas gratuits. La surcharge réelle varie selon les plates-formes, mais la création de threads prend du temps, introduisant une latence dans le traitement des demandes et nécessite une certaine activité de traitement de la part de la JVM et du système d'exploitation. Si les demandes sont fréquentes et légères, comme dans la plupart des applications serveur, la création d'un nouveau thread pour chaque demande peut consommer des ressources informatiques importantes.
De Java Concurrency in Practice
Par Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, Doug Lea
Print ISBN-10: 0-321-34960-1
la source
Réponses:
La création de threads Java est coûteuse car il y a pas mal de travail impliqué:
Il est également coûteux en ce sens que le fil attache les ressources tant qu'il est vivant; par exemple, la pile de threads, tous les objets accessibles depuis la pile, les descripteurs de thread JVM, les descripteurs de thread natifs du système d'exploitation.
Les coûts de toutes ces choses sont spécifiques à la plate-forme, mais ils ne sont pas bon marché sur aucune plate-forme Java que je connaisse.
Une recherche sur Google m'a trouvé un ancien benchmark qui rapporte un taux de création de threads d'environ 4000 par seconde sur un Sun Java 1.4.1 sur un Xeon à double processeur vintage 2002 exécutant 2002 vintage Linux. Une plate-forme plus moderne donnera de meilleurs chiffres ... et je ne peux pas commenter la méthodologie ... mais au moins, cela donne une idée du coût probable de la création de threads.
L'analyse comparative de Peter Lawrey indique que la création de threads est beaucoup plus rapide de nos jours en termes absolus, mais on ne sait pas dans quelle mesure cela est dû aux améliorations de Java et / ou du système d'exploitation ... ou à des vitesses de processeur plus élevées. Mais ses chiffres indiquent toujours une amélioration de plus de 150 fois si vous utilisez un pool de threads par rapport à la création / démarrage d'un nouveau thread à chaque fois. (Et il fait remarquer que tout cela est relatif ...)
(Ce qui précède suppose des "threads natifs" plutôt que des "threads verts", mais les JVM modernes utilisent tous des threads natifs pour des raisons de performances. Les threads verts sont peut-être moins chers à créer, mais vous payez pour cela dans d'autres domaines.)
J'ai creusé un peu pour voir comment la pile d'un thread Java est vraiment allouée. Dans le cas d'OpenJDK 6 sous Linux, la pile de threads est allouée par l'appel à
pthread_create
qui crée le thread natif. (La JVM ne transmet pas depthread_create
pile pré-allouée.)Puis, à l'intérieur
pthread_create
la pile est allouée par un appel àmmap
comme suit:Selon
man mmap
, l'MAP_ANONYMOUS
indicateur provoque l'initialisation de la mémoire à zéro.Ainsi, même s'il n'est peut-être pas essentiel que les nouvelles piles de threads Java soient mises à zéro (selon la spécification JVM), en pratique (au moins avec OpenJDK 6 sous Linux) elles le sont.
la source
malloc()
fonction C standard , que la JVM pourrait très bien utiliser, ne garantit pas que la mémoire allouée soit remise à zéro (probablement pour éviter de tels problèmes de performances).mmap()
appel soient mappées en copie sur écriture sur une page zéro, de sorte que leur initialisation ne se produit pas enmmap()
elle-même, mais lorsque les pages sont écrites pour la première fois , puis une seule page à un temps. Autrement dit, lorsque le thread démarre l'exécution, le coût est assuré par le thread créé plutôt que par le thread créateur.D'autres ont discuté de l'origine des coûts de filetage. Cette réponse explique pourquoi la création d'un thread n'est pas si coûteuse par rapport à de nombreuses opérations, mais relativement coûteuse par rapport aux alternatives d'exécution de tâches, qui sont relativement moins chères.
L'alternative la plus évidente à l'exécution d'une tâche dans un autre thread consiste à exécuter la tâche dans le même thread. Cela est difficile à comprendre pour ceux qui supposent que plus de threads sont toujours meilleurs. La logique est que si la surcharge liée à l'ajout de la tâche à un autre thread est supérieure au temps que vous enregistrez, il peut être plus rapide d'exécuter la tâche dans le thread actuel.
Une autre alternative consiste à utiliser un pool de threads. Un pool de threads peut être plus efficace pour deux raisons. 1) il réutilise les threads déjà créés. 2) vous pouvez régler / contrôler le nombre de threads pour garantir des performances optimales.
Le programme suivant imprime ...
Il s'agit d'un test pour une tâche triviale qui expose la surcharge de chaque option de thread. (Cette tâche de test est le type de tâche qui est en fait le mieux exécuté dans le thread actuel.)
Comme vous pouvez le voir, créer un nouveau thread ne coûte que ~ 70 µs. Cela pourrait être considéré comme trivial dans de nombreux cas d'utilisation, sinon dans la plupart. Relativement parlant, c'est plus cher que les alternatives et pour certaines situations, un pool de threads ou ne pas utiliser de threads du tout est une meilleure solution.
la source
En théorie, cela dépend de la JVM. En pratique, chaque thread a une quantité relativement importante de mémoire de pile (256 Ko par défaut, je pense). De plus, les threads sont implémentés comme des threads OS, donc leur création implique un appel OS, c'est-à-dire un changement de contexte.
Sachez que «cher» en informatique est toujours très relatif. La création de threads est très coûteuse par rapport à la création de la plupart des objets, mais pas très chère par rapport à une recherche aléatoire sur le disque dur. Vous n'avez pas à éviter à tout prix de créer des threads, mais en créer des centaines par seconde n'est pas une décision intelligente. Dans la plupart des cas, si votre conception nécessite beaucoup de threads, vous devez utiliser un pool de threads de taille limitée.
la source
K
= 1024 etk
= 1000.;) en.wikipedia.org/wiki/KibibyteIl existe deux types de threads:
Les threads appropriés : ce sont des abstractions autour des fonctionnalités de thread du système d'exploitation sous-jacent. La création de threads est donc aussi coûteuse que celle du système - il y a toujours une surcharge.
Threads "verts" : créés et programmés par la JVM, ils sont moins chers, mais aucun paralellisme approprié ne se produit. Ceux-ci se comportent comme des threads, mais sont exécutés dans le thread JVM du système d'exploitation. Ils ne sont pas souvent utilisés, à ma connaissance.
Le facteur le plus important auquel je puisse penser dans la surcharge de création de threads est la taille de pile que vous avez définie pour vos threads. La taille de la pile de threads peut être transmise en tant que paramètre lors de l'exécution de la machine virtuelle.
En dehors de cela, la création de threads dépend principalement du système d'exploitation, et même de l'implémentation de la VM.
Maintenant, permettez-moi de souligner quelque chose: la création de threads coûte cher si vous prévoyez de lancer 2000 threads par seconde, chaque seconde de votre runtime. La JVM n'est pas conçue pour gérer cela . Si vous avez quelques ouvriers stables qui ne seront pas licenciés et tués encore et encore, détendez-vous.
la source
La création
Threads
nécessite d'allouer une bonne quantité de mémoire car elle doit créer non pas une, mais deux nouvelles piles (une pour le code java, une pour le code natif). Utilisation d' exécuteurs / pools de threads peut éviter la surcharge, en réutilisant les threads pour plusieurs tâches pour Executor .la source
De toute évidence, le nœud de la question est de savoir ce que signifie «cher».
Un thread doit créer une pile et initialiser la pile en fonction de la méthode d'exécution.
Il doit mettre en place des structures d'état de contrôle, c'est-à-dire dans quel état il est exécutable, en attente, etc.
Il y a probablement beaucoup de synchronisation autour de la configuration de ces choses.
la source