Подтвердить что ты не робот

Servlet "запустил поток, но не смог его остановить" - утечка памяти в Tomcat

Apache Tomcat говорит много раз:

В веб-приложении [/MyServlet] появился поток с именем [pool-61-thread-2], но он не смог его остановить. Вероятно, это приведет к утечке памяти.

Это опасно? Сервлет должен иметь возможность обрабатывать 10.000 запросов/день. Как закрыть потоки, когда они закончили?

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();
4b9b3361

Ответ 1

Да, это проблема. Если ваш код запускает не-демона, то эти потоки будут продолжать работать до тех пор, пока они не выйдут из своего метода запуска. Даже если все остальное закончится, старая JVM будет зависеть, пока эти потоки продолжатся. Если вы запускаете новый экземпляр, тогда у вас может быть ситуация, когда старые потоки все еще работают вместе с созданными новым экземпляром.

Задачи должны быть спроектированы так, чтобы они реагировали на прерывание (в отличие от того, чтобы есть исключение и продолжалось, что показывает ваш пример). Это означает проверку прерванного флага в текущем потоке и удобное извлечение InterruptedException, что позволяет задаче отключить его работу, а также, при необходимости, сбрасывает прерванный флаг. Реализации ExecutorService имеют метод shutdownNow, который прервет текущие задачи.

Вот пример того, как остановить поток с прерыванием.

Убедитесь, что исполнитель отключен, вы можете обработать это в ServletContextListener.