メイン

2006年10月11日

やりかけの仕事が終わるまでプロセスの終了を遅らせる

プログラムによっては、外部からの終了イベント(Ctrl-Cの入力など)でいきなり終わってしまっては困るものが有ります。そこでJavaでは、JVMの終了時に実行したい処理を仕込むためのシャットダウンフックという仕組みが用意されています。

手元に、リクエストを受け付けてマルチスレッドでタスクを実行するプログラムが有ります。このプログラムの場合、やりかけのタスク(とキューに溜っているタスク)が全部終了してからプロセスを終了したいので、シャットダウンフック内でタスク実行スレッドの終了を待つようにしました。スレッドの管理をjava.util.concurrent.ExecutorServiceで行なうようにするとこれは意外と簡単に実現できます。

以下は、そのプログラムを簡単化したサンプルです。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ShutdownHookTest {
    public static void main(String[] args) throws Exception {
        final ExecutorService threadPool = Executors.newFixedThreadPool(3);
        Runtime.getRuntime().addShutdownHook(
            new Thread() {
                public void run() {
                    System.out.println("shutdown hook start");
                    // スレッドプールのシャットダウン
                    // 実行中のタスクとキュー内のタスクをすべて終えたら終了する
                    threadPool.shutdown();
                    try {
                        //すべてのタスクの終了を待つ
                        threadPool.awaitTermination(10, TimeUnit.SECONDS);
                    } catch (InterruptedException e) {
                    }
                    System.out.println("shutdown hook end");
                }
            });
		
        for (int i = 1; i <= 4; i++) {
            threadPool.execute(new Task(i));
        }
    }
}

// 各タスクは自分に与えられた番号と同じ秒数カウントして終了する
class Task extends Thread {
    int num;
    public Task(int num) {
       System.out.println("task #" + num + " init");
        this.num = num;
    }
    public void run() {
        System.out.println("task #" + num + " start");
        for (int i = 1; i <= num; i++) {
            try {
                Thread.sleep(1000);
                System.out.println("[" + num + "]" + i);
            } catch (InterruptedException e) {
            }
        }
        System.out.println("task #" + num + " end");
    }
}

[実行結果]

$java -cp bin ShutdownHookTest
main start
task #1 init
task #2 init
task #3 init
task #4 init
task #1 start
task #2 start
task #3 start              ←実行スレッドが3つなので、4つめのタスクはキューで待つ
shutdown hook start    ←Ctrl-Cを入力
[1]1
task #1 end
task #4 start              ←キューに溜っていたタスクがスタート
[2]1
[3]1
[4]1
[2]2
task #2 end
[3]2
[4]2
[3]3
task #3 end
[4]3
[4]4
task #4 end
shutdown hook end       ← 全部のタスクの実行が終わったので終了