2011-01-05 4 views
1

Voici un extrait d'un serveur Web que je suis en train de construire ...comportement avec plusieurs threads Peculiar/variables volatiles/boucles/conditionals (Java)

// ...

threadPool = Executors.newCachedThreadPool(); 
while (true) 
    if(this.isOn) { 
       try { // listen for incoming connection 
       this.clientSocket = serverSocket.accept(); 
     } catch (IOException e) { 
        System.err.println("LOG: >> Accept failed! "); 
        System.exit(1); 
        } 
     // as soon as a connection is established send the socket 
     // with a handler/processor to the thread pool for execution 

        threadPool.execute(new ClientRequestProcessor(clientSocket)); 
     } 

// ...

Veuillez noter que la variable isOn est un booléen VOLATILE.

Si j'allume le si dans un certain temps ... ce code fonctionne ... mais comme il est pas. Puis-je demander pourquoi? D'un point de vue logique, les deux devraient fonctionner, même si je teste ce drapeau dans un si ... ai-je raté quelque chose ?!

[Modification postérieure:] En ne fonctionnant pas, je veux dire ... un navigateur (par exemple firefox) ne peut pas se connecter, en fait il continue à essayer mais il finira par s'arrêter. Encore une fois, si je change ça si (isOn) en un temps (isOn) cela fonctionne comme un charme.

Toutes les suggestions/idées sont plus que bienvenues !!!

P.S. J'ai besoin de ce combo "while (true) if/while (drapeau de test) {...}" parce que le serveur peut être démarré/arrêté à partir d'une interface graphique ... donc le niveau supérieur alors que (vrai) est nécessaire peut revérifier si je suis sur (et donc écouter les connexions) ou si je suis éteint (et ne se soucient pas vraiment des connexions entrantes). Les aiguilles indiquant que les gestionnaires d'événements de l'interface graphique peuvent modifier le drapeau à tout moment.

Répondre

2

Une meilleure solution consiste à fermer le socket du serveur lorsque vous voulez qu'il s'arrête et en lancer un nouveau dans un nouveau thread lorsque vous voulez le démarrer. De cette façon, vous rejetez les nouvelles connexions et ne consommez pas de CPU lorsque vous ne faites rien.

Lorsque isOn == true et que vous le désactivez, il n'acceptera que les connexions après la nouvelle connexion suivante. (Peut être n'importe quand plus tard) De plus, toute nouvelle connexion client attendra simplement que l'acceptation soit appelée (ou éventuellement expirée) Vous pouvez avoir jusqu'à 50 connexions en attente d'être acceptées par défaut.

Lorsque isOn == false, votre thread sera occupé, consommant un CPU. Je vous suggère de mettre un peu de retard comme Thread.sleep (250). Cela va couper le processeur de façon spectaculaire, mais ne retardera pas trop le démarrage.

BTW:

  • si vous obtenez une exception, vous devez vous connecter/imprimer. Sinon, quand cela échouera, vous ne saurez pas pourquoi.
  • Si l'acceptation échoue, il se peut que le processus soit en dehors des fichiers, vous ne voulez donc pas qu'il meure simplement, en supprimant toutes les connexions existantes.
+0

Je suis complètement d'accord avec ce que vous avez dit. De plus, vous avez raison de dire que le changement de drapeau à false garantit encore une autre "écoute d'une connexion" avant que le serveur "arrête de servir". Mais cela n'explique toujours pas le comportement erratique que j'obtiens avec la version actuelle du code! Je vais probablement adopter votre solution, mais je suis toujours curieux de savoir pourquoi une version fonctionne et l'autre ne fonctionne pas ... alors que les deux devraient logiquement l'être. Les questions n'étaient pas vraiment s'il y a une meilleure solution (vous avez déjà prouvé qu'il existe!) Mais plutôt pourquoi ce code Java se comporte-t-il si maladroitement ... – Tibbers

+0

@Tibi, while (true) if (flag) et while (true) alors que (drapeau) devrait être identique dans votre cas. Je soupçonne que votre test n'est pas stable reproductible. –

+0

Encore une fois, vous pourriez avoir raison sur celui-ci aussi. Parce que si j'exécute le if-variant avec le débogueur et que j'exécute le code ligne par ligne alors ça marche parfaitement ... cependant si je viens de lancer run et de le laisser seul le if-variant ne fonctionne pas! Donc, en effet, je ne peux probablement pas reproduire l'état réel de la machine, rendant ainsi toute tentative de comprendre ce comportement maladroit futile. Quoi qu'il en soit, merci pour les réponses rapides ... votre proposition (initiale) de refactoring/redesign a déjà été implémentée et fonctionne comme un charme! – Tibbers

0

Si vous avez le while (true) et si if (this.isOn) la boucle while n'a aucun moyen d'arrêter. Que se passe-t-il lorsque le drapeau isOn est mis à false. La boucle while ne s'arrête jamais car elle est essentiellement faite pour être infinie. Branchez une autre instruction pour la faire sauter et cela devrait fonctionner comme prévu.

Si vous supprimez l'instruction if et que vous la faites juste while (this.isOn), lorsque l'indicateur isOn est mis à false, la boucle se termine. Pas de boucle infinie.

Ce sont mes pensées au premier coup d'œil ...

+0

Salut! Avez-vous lu mon P.S.? Quand je dis tourner le si dans un temps je veux dire >>>> while (true) while (this.isOn) {...} << et cela fonctionne comme ça ... comme dans la connexion est établie avec succès! – Tibbers

+0

il ne veut pas quitter la boucle, il suffit de tourner autour du while (true) en attendant que le if devienne vrai. Le comportement inattendu est que if (isOn) ne devient jamais vrai et donc aucune tentative n'est faite pour accepter() mais si à la place il a while (isOn) c'est le cas. – djna

+0

Vous êtes partiellement correct djna. Le débogueur dans eclipse dit que le drapeau devient en effet vrai. Et à un moment donné envoie ce nouveau gestionnaire au pool de threads. Mais pour une raison quelconque, cela ne fonctionne pas. Cependant si je change cela si dans un moment il fonctionne miraculeusement (sans aucune autre modification n'importe où dans le code)! – Tibbers

0

Mon hypothèse serait basée sur votre là boucle serrée. Est-il possible que vous ayez plusieurs instances du programme en cours d'exécution sur votre serveur? La version if (isOn) ne s'arrêtera pas si vous définissez isOn sur false, mais bouclez simplement votre CPU.