tanamonの稀に良く書く日記

KEEP CALM AND DRINK BEER

Javaではメインスレッドが終了してもプログラムは終了しませんよ

はい、今日のバグでした。

だいたいこんなコード

final ExecutorService executor = Executors.newFixedThreadPool(50);
try {
    searchHoge(new HogeCallback() {
        public void call(Hoge hoge) {
            executor.execute(new HogeThread(hoge));
        }
    });
} catch(Exception e) {
    e.printStackTrace();
}

executor.shutdown();
try {
    executor.awaitTermination(30, TimeUnit.MINUTES);
} catch (InterruptedException e) {}

作っていたプログラムでは、全てのデータを探すのを待ってからだと効率が悪いので

  • 見つかるたびにHogeCallbackにデータを返す
  • データは別スレッドで処理する

という実装にしていた。


で、何に引っかかったかというと、searchHogeの処理はtry - catchをしているんだけど、catchをExceptionで指定していたのでError系(今回はOutOfMemoryError)がスルーされていた。そのため、executor.shutdown()が呼ばれずメインスレッドが終了した後も、executorは永久に来ない仕事を待ち続けていたため、プロセスは終了しなかった、と。


今日の教訓。
executorはいつでもfinallyで書く癖を付けた方がいい。


以下、Javaでメインスレッドが終了してもプログラムが終了しない検証。

Test.java

public class Test {
    public static void main(String[] args) {
        new Thread() {
            public void run() {
                try {
                    Thread.sleep(10 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread end.");
            }
        }.start();
        System.out.println("main end.");
    }
}

ちゃんと"thread end."が出力された後にプログラムが終了する。


ちなみにRubyの場合。

test.rb

Thread.new do
  sleep 10
  puts 'thread end.'
end
puts 'main end.'

'thread end.'は出力されずに終わる。