web-dev-qa-db-ja.com

サーブレットは「スレッドを開始しましたが、停止に失敗しました」-Tomcatのメモリリーク

Apache Tomcatは何度も言っています:

Webアプリケーション[/ MyServlet]は[pool-61-thread-2]という名前のスレッドを開始したようですが、停止に失敗しました。これは、メモリリークを引き起こす可能性が非常に高いです。

これは危険ですか?サーブレットは、1日あたり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();
15
user3876178

はい、それは問題です。コードが非デーモンスレッドを開始する場合、それらのスレッドはrunメソッドを終了するまで動作し続けます。他のすべてが終了したとしても、古いJVMはそれらのスレッドが継続している間ハングアップします。新しいインスタンスを起動すると、新しいインスタンスによって作成されたスレッドと一緒に古いスレッドがまだ動作している状態になる可能性があります。

タスクは、割り込みに反応するように設計する必要があります(例外を食べて続行するのではなく、これは例で示しています)。つまり、現在のスレッドの割り込みフラグをチェックし、タスクが作業を中断できるように役立つ方法でInterruptedExceptionをキャッチし、必要に応じて割り込みフラグをリセットすることを意味します。 ExecutorServiceの実装には、現在のタスクを中断するshutdownNowメソッドがあります。

割り込みを使用してスレッドを停止する方法の例

Executorがシャットダウンされることを確認してください ServletContextListenerでこれを処理できます

16
Nathan Hughes