Javaで同じプログラムの同時実行をプロセス数で制限する方法

こんにちは、さるまりんです。

同じプログラムを同時に実行することは頻繁に発生します。同時に処理が動いては困る時、複数の同時実行は大丈夫なんだけどいっぱい立ち上がりすぎても困る時、いろいろです。

今回はプログラムの処理を実行するか否かを自身からプロセス数をチェックして判断するプログラムを作ってみました。

com.salumarine.app.BigFileProcessorは大きなファイルを処理して時間のかかるプログラムです。この処理を同時に3つまでの実行に制限しようと思います。

以下、プログラムです。

import java.util.Optional;

public class BigFileProcessor {
    public static void main(String[] args) {
        // プロセス制限数
        final int MAX_PROCESSES = 3;

        // 対象のプログラムを識別するコマンドライン
        final String TARGET_COMMAND = "com.salumarine.app.BigFileProcessor";

        try {
            // 現在のプロセスID
            long currentPid = ProcessHandle.current().pid();

            // 現在のプロセスのコマンドラインを確認
            Optional<String> currentCommandLine = ProcessHandle.current()
                    .info()
                    .commandLine();

            // 対象のプログラムか確認
            if (currentCommandLine.isEmpty() || !currentCommandLine.get().contains(TARGET_COMMAND)) {
                System.out.println("このプロセスは対象ではありません。処理を終了します。");
                System.exit(0);
            }

            // 同じプログラムが実行中のプロセス数をカウント
            long processCount = ProcessHandle.allProcesses()
                .filter(process -> {
                    try {
                        // プロセスのコマンドラインを取得
                        Optional<String> commandLine = process.info().commandLine();
                        // 条件: 対象のプログラム名を含み、自分自身ではない
                        return commandLine.isPresent() &&
                               commandLine.get().contains(TARGET_COMMAND) &&
                               process.pid() != currentPid;
                    } catch (Exception e) {
                        return false;
                    }
                })
                .count();

            // 制限を超えていたら終了
            if (processCount >= MAX_PROCESSES) {
                System.out.println("すでに " + MAX_PROCESSES + " プロセスが実行中です。終了します。");
                System.exit(0);
            }

            // メイン処理を実行
            System.out.println("プロセス数が許容範囲内です。プログラムを開始します。");
            runMyOwnTask(); // 実際の処理

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 実際のプログラム内容
    private static void runMyOwnTask() {
        System.out.println("大きなファイルを処理しています...");
        // ここにファイル処理ロジックを記述
    }
}

プログラム中のコメントにもありますが、やっていることを簡単に説明します。

MAX_PROCESSESは同時実行可能なプロセス数、TARGET_COMMANDはプログラムのクラス名です。

ProcessHandle.current().info().commandLine()を使用して現在のプロセスのコマンドラインを取得し、TARGET_COMMANDが含まれているかをチェックします。

ProcessHandle.allProcesses()で現在実行中のすべてのプロセスを取得して、各プロセスのコマンドラインを指定の条件(自分自身を除外したコマンドのクラス名一致するプロセス)でチェックし該当するプロセスを数えます。

起動している同じプログラムのプロセス数がMAX_PROCESSES、今回の場合は3以上の場合はSystem.exit(0);で自分自身を終了しています。そうでない場合はrunMyOwnTask();で実際の処理を行います。

以下のコマンド

java com.salumarine.app.BigFileProcessor

で実行すると、プロセスが制限数内の場合は

プロセス数が許容範囲内です。プログラムを開始します。
大きなファイルを処理しています...

と出力され、制限を超えている場合は

すでに 3 プロセスが実行中です。終了します。

と表示されます。

TARGET_COMMANDは今回クラス名にしていますが、コマンドラインから取得できる文字列によっては調整が必要かもしれないです。また、場合によってはcontains()ではなくequals()に変えることができるかもしれません。この辺りは実際に作って走らせながら確認が必要です。

今回は同じプログラムがたくさん立ち上がってしまいCPUもメモリもいっぱい使ってしまってシステムが不安定になってしまったので、そうならないようにプロセス数を制限したくてこのような方法をとりました。
java [クラス名]で実行されるプログラム内でこの制御をするよりも、そのプログラムを起動するためのシェルスクリプトでプログラム自体の起動を制限した方が良いかもしれませんね。今度はそれをやってみたいと思います。

読んでくださってありがとうございました。
それではまた!