Disons que nous avons le programme C ++ suivant
public class HelloWorld { public static void main(String[] args) { Thread t = new Thread(new Hello()); t.start(); } } class Hello implements Runnable { public void run() { System.out.println("Hello") ; } }
Si nous n'appelons pas join dans ce code, notre programme plantera car le thread principal se terminera avant que le thread t1 ne soit terminé. Mais si nous avons le même programme en Java, le programme s'exécute normalement même si le main n'attend pas le thread.
void hello (){ std :: cout << "HELLO"<<std::endl ; } int main(){ std:: thread t(hello) ; t.join() ; }
Alors pourquoi en Java le programme ne plante pas? comment le thread est exécuté même si le principal se termine en premier?
3 Réponses :
C'est assez simple: contrairement au C ++, qui se termine une fois que main
est terminé, un programme java ne se termine que si tous les threads (non-démon) (y compris main) sont terminés (cf., pour exemple, cette documentation de thread oracle):
Lorsqu'une machine virtuelle Java démarre, il y a généralement un seul thread non démon (qui appelle généralement la méthode nommée main de certains classe désignée). La machine virtuelle Java continue de s'exécuter threads jusqu'à ce que l'un des événements suivants se produise:
a. La méthode de sortie de la classe Runtime a été appelée et la sécurité manager a autorisé l'opération de sortie.
b. Tous les threads qui ne sont pas des threads démons sont morts, soit par retour de l'appel à la méthode run ou en lançant une exception qui se propage au-delà de la méthode run.
C ++, en revanche, commencera à détruire les objets avec une durée de stockage statique, et si un thread détaché est toujours en cours d'exécution et accède à ces objets, cela donne un comportement indéfini (cf., par exemple, ce projet de norme C ++ concernant l'arrêt du programme):
3.6.3 Résiliation
Destructeurs ([class.dtor]) pour les objets initialisés (c'est-à-dire les objets dont la durée de vie ([basic.life]) a commencé) avec une durée de stockage statique sont appelés à la suite du retour de main et à la suite de appelant std :: exit ([support.start.term]).
Ceci est dû au fait que JVM a des règles différentes pour la fin du processus de C / C ++. Sur JVM, le processus est arrêté une fois qu'il n'y a plus de threads non démon en cours d'exécution. Une application C ou C ++ est arrêtée une fois que sa fonction main
est renvoyée. Voir Si vous revenez du thread principal, le processus se termine-t-il? < / a>:
la bibliothèque d'exécution C appelle automatiquement ExitProcess lorsque vous quittez le thread principal, qu'il y ait ou non des threads de travail encore actifs. Ce comportement pour les programmes de console est imposé par le langage C, qui dit que (5.1.2.2.3) "un retour de l'appel initial à la fonction main équivaut à appeler la fonction exit avec la valeur retournée par la fonction main comme son argument." Le langage C ++ a une exigence équivalente (3.6.1). Vraisemblablement, les responsables de l'exécution C ont transmis ce comportement à WinMain pour des raisons de cohérence.
Le comportement C est comme si vous appeliez System.exit
à la fin de votre main
Java.
La réponse directe est la suivante: votre application plante car std :: thread
appelle std :: terminate
dans son destructeur si le thread n'a pas été joint et n'est pas détaché. C'est considéré comme un vilain bogue, d'oublier silencieusement les threads non détachés.
Vous pouvez éviter un crash immédiat si vous détachez le thread avant de revenir de main. Cependant, vous risquez toujours d'avoir toutes sortes de feux d'artifice possibles en raison de l'accès à std :: cout
- qui est un objet global, et qui sera détruit après le retour de main code >, potentiellement pendant que votre fil y accède encore.
Remarque: les programmeurs C ++ s'attendent à ce que les destructeurs d'objets ferment les fichiers, libèrent de la mémoire et nettoient généralement les choses. Dans certains univers fantastiques, nous aimerions que le destructeur std :: thread
tue simplement le thread sans faire d'histoires. Mais comme en réalité, il n'y a pas de moyen raisonnable de "simplement tuer" un fil, il fait la meilleure chose suivante, qui est de vérifier que l'appelant l'a déjà tué ou a accepté la responsabilité de le tuer à l'avenir (en appelant t.detach ()
.) Les programmeurs Java n'ont pas le luxe des destructeurs. Ils doivent nettoyer les choses plus explicitement.
java n'est pas c ++, pourquoi vous attendiez-vous à ce qu'ils soient les mêmes?
Pour être clair, le crash est dû au fait qu'un thread non joint provoquera
std :: terminate
pour être appelé dans lestd :: thread
destructor .