2010-07-26 5 views
32

Le code suivant essaie de l'accompagner.Fermeture progressive des threads et de l'exécuteur

Le code boucle pour toujours et vérifie s'il y a des demandes en attente à traiter. S'il y en a, il crée un nouveau thread pour traiter la requête et la soumet à l'exécuteur. Une fois tous les threads terminés, il dort pendant 60 secondes et vérifie à nouveau les demandes en attente.

public static void main(String a[]){ 
    //variables init code omitted 
    ExecutorService service = Executors.newFixedThreadPool(15); 
    ExecutorCompletionService<Long> comp = new ExecutorCompletionService<Long>(service); 
    while(true){ 
     List<AppRequest> pending = service.findPendingRequests(); 
     int noPending = pending.size(); 
     if (noPending > 0) { 
      for (AppRequest req : pending) { 
       Callable<Long> worker = new RequestThread(something, req); 
       comp.submit(worker); 
      } 
     } 
     for (int i = 0; i < noPending; i++) { 
      try { 
       Future<Long> f = comp.take(); 
       long name; 
       try { 
        name = f.get(); 
        LOGGER.debug(name + " got completed"); 
       } catch (ExecutionException e) { 
        LOGGER.error(e.toString()); 
       } 
      } catch (InterruptedException e) { 
       LOGGER.error(e.toString()); 
      } 
     } 
     TimeUnit.SECONDS.sleep(60); 
    } 

    } 

Ma question est la plupart du traitement effectué par ces threads traitent avec la base de données. Et ce programme fonctionnera sur une machine Windows. Qu'arrive-t-il à ces threads lorsque quelqu'un tente d'arrêter ou de déconnecter la machine? Comment fermer gracieusement les threads en cours d'exécution et aussi l'exécuteur?

Répondre

55

Un arrêt ordonné typique d'un ExecutorService pourrait ressembler à ceci:

final ExecutorService executor; 

Runtime.getRuntime().addShutdownHook(new Thread() { 
    public void run() { 
     executor.shutdown(); 
     if (!executor.awaitTermination(SHUTDOWN_TIME)) { //optional * 
      Logger.log("Executor did not terminate in the specified time."); //optional * 
      List<Runnable> droppedTasks = executor.shutdownNow(); //optional ** 
      Logger.log("Executor was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed."); //optional ** 
     } 
    } 
}); 

* Vous pouvez vous connecter que l'exécuteur avait encore des tâches à traiter après avoir attendu le moment où vous étiez prêt à attendre.
** Vous pouvez tenter de forcer les threads de travail de l'exécuteur à abandonner leurs tâches en cours et à s'assurer qu'ils ne démarrent aucun des threads restants.

Notez que la solution ci-dessus fonctionnera lorsqu'un utilisateur émet une interruption à votre processus java ou lorsque votre ExecutorService contient uniquement des threads de démon. Si, au lieu de cela, ExecutorService contient des threads non démon qui n'ont pas été terminés, la machine virtuelle Java n'essaie pas de s'arrêter et, par conséquent, les hooks d'arrêt ne seront pas invoqués. Si vous tentez d'arrêter un processus dans le cadre d'un cycle de vie d'application discret (pas un service), le code d'arrêt ne doit pas être placé dans un point d'arrêt mais à l'emplacement approprié où le programme est conçu pour se terminer.

+0

Ceci est en arrière. En supposant que 'ExecutorService' est l'une des valeurs typiques renvoyées par les méthodes d'usine' Executors', les threads de backing sont des threads non-daemon. Le hook de fermeture ne sera pas appelé jusqu'à ce que ces threads se terminent, et cela ne se produira pas tant que 'ExecutorService' ne sera pas arrêté. Votre solution ne fonctionnera que si les threads de l'exécuteur sont non démon ou si votre programme reçoit une interruption de l'utilisateur. –

+0

L'OP demande spécifiquement si la JVM a été appelée à effectuer une fermeture ordonnée. Quoi qu'il en soit, le contenu de la réponse est précis, il suffit de ne pas le faire dans un hook de fermeture pour terminer le service de l'exécuteur plus tôt. –

+0

Merci d'avoir répondu si rapidement.Je suppose que j'ai raté cette partie de leur question. Pouvez-vous ajouter un troisième '***' pour clarifier ce bit? –

7

Le livre "Java Concurrency in Practice" déclare:

7,4. Arrêt de la JVM

La machine virtuelle Java peut être arrêtée de manière ordonnée ou brutale, soit en . Un arrêt ordonnée est déclenchée lorsque le dernier fil (nondaemon) « normal » termine, quelqu'un appelle System.exit, ou par d'autres moyens spécifiques à la plateforme (telles que l'envoi d'un SIGINT ou frapper Ctrl-C). [...]

7.4.1. Fermeture des crochets

Lors d'une fermeture ordonnée, la JVM commence par tous les crochets d'arrêt enregistrés. Les crochets d'arrêt sont des threads non démarrés enregistrés avec Runtime.addShutdownHook. La JVM ne fait aucune garantie sur l'ordre dans lequel les crochets d'arrêt sont démarrés. Si des threads d'application (daemon ou non-monon) sont toujours en cours d'exécution à , ils continuent à exécuter en même temps que le processus d'arrêt . Lorsque tous les hooks d'arrêt ont terminé, la JVM peut choisir d'exécuter les finaliseurs si runFinalizersOnExit est true, puis s'arrête. La machine virtuelle Java n'effectue aucune tentative d'arrêt ou d'interruption des threads d'application qui sont toujours en cours d'exécution à l'arrêt; ils sont brusquement terminés lorsque la JVM s'arrête finalement. Si les crochets d'arrêt ou les finaliseurs ne se terminent pas, alors le processus d'arrêt normal "se bloque" et la JVM doit être arrêtée brutalement . [...]

Les bits importants sont, « La machine virtuelle Java ne fait aucune tentative pour arrêter ou interrompre les threads d'application qui sont encore en cours d'exécution au moment de l'arrêt,. Ils sont brusquement pris fin lorsque la machine virtuelle Java arrête finalement » donc je suppose que la connexion à la DB se terminera brusquement, si aucun hook d'arrêt n'est là pour faire un nettoyage gracieux (si vous utilisez des frameworks, ils fournissent généralement de tels hooks d'arrêt). Dans mon expérience, la session à la base de données peut rester jusqu'à ce qu'elle soit dépassée par la base de données, etc. lorsque l'application. est terminé sans ces crochets.

2

Vous pouvez appeler shutdown() sur le ExecutorService:

lance un arrêt ordonné dans lequel tâches précédemment soumises sont exécutées, mais aucune nouvelle tâche sera acceptée.

ou vous pouvez appeler shutdownNow():

tente d'arrêter tous activement exécution des tâches, le traitement arrête le des tâches en attente, et retourne une liste des tâches qui étaient en attente d'exécution .

Il n'y a aucune garantie au-delà de de meilleurs efforts pour arrêter de traitement des tâches en cours d'exécution. Par exemple, les implémentations types annuleront via Thread.interrupt(), donc toute tâche qui ne répond pas aux interruptions ne peut jamais se terminer.

Lequel vous appelez dépend à quel point vous voulez arrêter ....

+0

Je suis confronté à un problème suivant dans mon application web.Comment arrêter les fuites de mémoire. Http: //stackoverflow.com/questions/39166512/memory-leak-error-in-tomcat-server-even-after-removed-quartz -related-code – Vicky

5

Depuis l'ajout d'un crochet d'arrêt pour appeler explicitement shutdown() n'a pas fonctionné pour moi, j'ai trouvé une solution facile dans Google Guava: com.google.common.util.concurrent.MoreExecutors.getExitingExecutorService.

+1

Néanmoins, c'est un peu problématique car 'MoreExecutors.getExitingExecutorService' n'accepte que des instances de' ThreadPoolExecutor'. Par conséquent, vous devez définir manuellement ces exécuteurs au lieu de simplement appeler, par exemple, 'Executors.newFixedThreadPool' qui retourne' ExecutorService'. – voo

Questions connexes