web-dev-qa-db-fra.com

Servlet "a démarré un thread mais n'a pas pu l'arrêter" - fuite de mémoire dans Tomcat

Apache Tomcat dit plusieurs fois:

L'application Web [/ MyServlet] semble avoir démarré un thread nommé [pool-61-thread-2] mais n'a pas pu l'arrêter. Il y a beaucoup de chances que cela génère des fuites mémoires.

Est-ce dangereux? Le servlet doit pouvoir traiter 10 000 demandes/jour. Comment fermer les fils quand ils ont fini?

class Worker {

        private final CountDownLatch startSignal;
        private final CountDownLatch doneSignal;
        private final int threadNumber;

        Worker(
                CountDownLatch startSignal,
                CountDownLatch doneSignal,
                int threadNumber
        ){

            this.startSignal = startSignal;
            this.doneSignal = doneSignal;
            this.threadNumber = threadNumber;

        }

        public String[][] getSomeStrArrArr() {

            String[][] isRs = new String[8][20];
            String[][] inRs = new String[8][20];
            String[][] iwRs = new String[8][20];

            try {

                startSignal.await();

                if (threadNumber == 1) {
                    // get String[][] result for thread number 1
                    isRs = getIS(erg1, erg2, request);

                }

                if (threadNumber == 2) {
                    // get String[][] result for thread number 2
                    inRs = getIN(search_plz, request);
                }

                if (threadNumber == 3) {
                    // get String[][] result for thread number 3
                    iwRs = getIW(erg1, erg2, request);
                }

                doneSignal.countDown();

            } catch (InterruptedException ex) {

                System.out.println(
                        "Thread number "+threadNumber+" has been interrupted."
                );

            }
            if (threadNumber == 1) {
                return isRs;
            }
            if (threadNumber == 2) {
                return inRs;
            }
            if (threadNumber == 3) {
                return iwRs;
            }
            return null;
        }


        public Callable<String[][]> getSomeCallableStrArrArr(){
            return new Callable<String[][]>() {
                public String[][] call() throws Exception {
                    return getSomeStrArrArr();
                }
            };
        }

    }

    ExecutorService pool = Executors.newFixedThreadPool(3);
    Set<Future<String[][]>> set = new HashSet<Future<String[][]>>();
    CountDownLatch startSignal = new CountDownLatch(1);
    CountDownLatch doneSignal = new CountDownLatch(3);
    for (int i=1;i<=3;i++) {
        Worker worker = new Worker(startSignal,doneSignal,i);
        Callable<String[][]> callable =
                worker.getSomeCallableStrArrArr();
        Future<String[][]> future = pool.submit(callable);
        set.add(future);
    }
    startSignal.countDown();
    try {
        doneSignal.await();
15
user3876178

Oui, c'est un problème. Si votre code démarre des threads non démons, ces threads continueront de fonctionner jusqu'à ce qu'ils quittent leur méthode d'exécution. Même si tout le reste se termine, l'ancienne JVM va rester pendant que ces threads continuent. Si vous démarrez une nouvelle instance, vous pouvez avoir une situation où les anciens threads fonctionnent toujours aux côtés de ceux créés par la nouvelle instance.

Les tâches doivent être conçues de manière à répondre aux interruptions (au lieu de manger l'exception et de continuer, comme le montre votre exemple). Cela signifie que la vérification de l'indicateur interrompu sur le thread en cours et la capture d'InterruptedException d'une manière utile qui permet à la tâche d'interrompre son travail et réinitialise également l'indicateur interrompu si nécessaire. Les implémentations ExecutorService ont une méthode shutdownNow qui interrompra les tâches en cours.

Voici un exemple de comment arrêter un thread en utilisant une interruption .

Assurez-vous que l'exécuteur est arrêté, vous pouvez gérer cela dans un ServletContextListener .

16
Nathan Hughes