1

私は現在、次のようなコードのチャンクをリファクタリングしています。

public static void main(String... args) {
    while (true) {
        // Do something...
        Thread.sleep(300000); // Every 5 minutes
    }
}

アイデアは、コードの実行に a を使用したいということScheduledExecutorServiceです:

public static void main(String... args) {
    ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
    service.scheduleAtFixedRate(new Task(...), 0, 5, TimeUnit.MINUTES);
}

ただし、これにはいくつかの懸念があります。まず、main()スケジューリング後にプログラムが「終了」する (リターンのように) という事実ですが、JVM はScheduledExecutorService実行されたままであるため、存続します。これは、「外部から終了されるまで実行し続ける」という古いコードの動作を維持するための意図的なものだと思いますが、ScheduledExecutorService適切にシャットダウンするためにシャットダウン フックをインストールする必要があると思います。それは実際に当てはまりますか、それとも「外部から殺される」ことでシャットダウンフックの実行も妨げられますか?

第二に、私はそもそもシャットダウンフックを持つことについてもきしむ。次のようなコードがあった場合

try {
    while (true) {}
} finally {
    // Shutdown hook code goes here instead
    service.shutdown();
    if (!service.awaitTermination(1, TimeUnit.SECONDS)) {
        service.shutdownNow();
    }
}

上記と同じ「外部から殺される」懸念はまだ存在しますか?

編集: はい、元のプログラムはループを突破することなくループします。たとえば、SIGINT(端末のcontrol-C)から終了することのみを目的としていました。かなり曖昧な終了条件ですが、今はそれしかありません...

4

3 に答える 3

2

あなたが示している2つのアプローチには根本的な違いがあります。これwhile(true)は同期プロセスです。その中でルーチンを実行すると、そのメインスレッドのタスクを制御できます。提案されたリファクタリング アプローチは、タスクをエグゼキュータにオフロードしているため (したがって、新しいスレッド)、タスクを非同期的に実行しています。

他のコメントで述べたように、エグゼキューターのスレッドは非デーモンで作成されます。つまり、アプリケーションでシャットダウンが呼び出されると、最終的なシャットダウンを完了する前にスレッドが終了するのを待ちます。

したがって、これを処理するために、executor のクリーン シャットダウンを処理するためにできることがいくつかあります。他の投稿で言及されたことを最初に行うには、終了条件を見つける必要があります。

次に、プロセスを評価し、プロセスの途中で停止できない重要なことを行っているかどうかを確認する必要があります (ディスク上のファイルへの書き込みなど)。

重要なことを何もしていない場合 (ここが重要な部分です) ThreadFactory、デーモン スレッドを返す a を渡すことができます。これにより、プロセスが完了しているかどうかにかかわらず、JVM はプロセスを終了します。ファクトリは次のようになります (スレッドに名前を付けるのが常に適切であるため、名前も追加しました。デバッグが容易になります)。

public class DaemonThreadFactory implements ThreadFactory {

    private final String NAME;

    public DaemonThreadFactory() {
        this(null);
    }

    public DaemonThreadFactory(final String name) {
        NAME = name;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setDaemon(true);
        if(NAME != null) {
        t.setName(NAME);
        }
        return t;
    }
}

それを処理できるもう 1 つの方法は、ScheduledFutureによって返されたを保持することservice.scheduleAtFixedRateです。このオブジェクトを使用すると、必要に応じてタスクをキャンセルし、実行プログラムをシャットダウンできます。

private void test() {
        ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();

        ScheduledFuture<?> future =  exec.scheduleAtFixedRate(new Task(), 0, 5, TimeUnit.MINUTES);

        //if you want to cancel the task
        //boolean is if you want to interrupt while running
        future.cancel(true);
    }

エグゼキューターをシャットダウンしてクリーンにする必要がありますが、この時点では何も実行されていないはずです

何らかのFuture理由でオブジェクトを使用できない場合、アプリケーションをシャットダウンするには、shutdown と shutdownNow を使用する必要があります。また、重要なことを行っている場合は、実行中のものを適切にクリーンアップして正常に終了するために、タスクでシャットダウン フックまたは非同期呼び出しが必要になります。

于 2014-08-08T19:00:08.753 に答える