6

私は次のことを達成したいと考えています: アプリケーションが起動すると、メイン スレッドはバックグラウンドで実行する必要がある 1 つ以上のワーカー スレッドを開始し、定期的に舞台裏で処理を行います。これらはメイン スレッドをブロックするべきではありません。

  1. メイン スレッドが終了します (通常のアプリケーション終了)。コマンド ライン ユーティリティの場合、これはmain(String[])メソッドの最後に到達したときです。Swing GUI の場合は、ユーザーがFile >> Exitメニューを選択したときなどです。
  2. オペレーティング システムが強制終了コマンド (SIGKILL など) をスローします。
  3. メインスレッドで予期せぬキャッチされていない例外が発生し、事実上それを強制終了します (これは上記の #1 の無礼なバージョンです)。

メインスレッドから開始/送信されたら、すべてのワーカースレッド ( Runnables) が本質的に独自のライフサイクルを持ち、メインスレッドとは独立して存在するようにします。しかし、いつでもメイン スレッドが停止した場合は、すべてのワーカーのシャットダウンが完了するまでメイン スレッドをブロックし (可能な場合)、メイン スレッドの停止を"許可" できるようにしたいと考えています。

これまでのところ私の最善の試みですが、あちこちでピースが欠けていることはわかっています:

public class MainDriver {
    private BaneWorker baneWorker;

    private ExecutorService executor = Executors.newCachedThreadPool();

    public static void main(String[] args) {
        MainDriver driver = new MainDriver();
        driver.run();

        // We've now reached the end of the main method. All workers should block while they shutdown
        // gracefully (if at all possible).
        if(executor.awaitTermination(30, TimeUnit.SECONDS))
            System.out.println("Shutting down...");
        else {
            System.out.println("Forcing shut down...");
            executor.shutdownNow();
        }
    }

    private void run() {
        // Start all worker threads.
        baneWorker = new BaneWorker(Thread.currentThread());
        // More workers will be used once I get this simple example up and running...

        executor.submit(baneWorker);
        // Eventually submit the other workers here as well...

        // Now start processing. If command-line utility, start doing whatever the utility
        // needs to do. If Swing GUI, fire up a parent JFrame and draw the application to the
        // screen for the user, etc.
        doStuff();
    }

    private void doStuff() {
        // ??? whatever
    }
}

public class BaneWorker implements Runnable {
    private Timer timer;

    private TimerTask baneTask;

    private Thread mainThread;

    public BaneWorker(Thread mainThread) {
        super();

        this.mainThread = mainThread;
    }

    @Override
    public void run() {
        try {
            timer = new Timer();

            baneTask = new TimerTask() {
                @Override
                public void run() {
                    System.out.println("When the main thread is ashes...");
                }
            };

            // Schedule the baneTask to kick off every minute starting now.
            timer.scheduleAtFixedRate(baneTask, new Date(), 60 * 1000);
        } catch(InterruptedException interrupt) {
            // Should be thrown if main thread dies, terminates, throws an exception, etc.
            // Should block main thread from finally terminating until we're done shutting down.
            shutdown();
        }
    }

    private void shutdown() {
        baneTask.cancel();

        System.out.println("...then you have my permission to die.");

        try {
            mainThread.join();
        } catch(InterruptedException interrupt) {
            interrupt.printStackTrace;
        }
    }
}

私はここで軌道に乗っていますか、それとも基地から外れていますか? これを必要な方法で機能させるには、何を変更する必要がありますか? 私は Java コンカレンシーを初めて使用し、コンカレンシー API を正しく使用するために最善を尽くしていますが、少しつまずきます。何か案は?前もって感謝します!

4

3 に答える 3

1

メイン スレッドは、ワーカー スレッドに終了するように通知する必要があり (通常、これはフラグを使用するだけで実現されます)、すべてのスレッドを呼び出しjoinて終了を待機する必要があります。こちらをご覧ください: Java: Thread.join の使用方法

于 2013-07-04T14:43:53.550 に答える
1

Runtime.addShutdownHookを使用して、JVM の終了時やシステムのシャットダウン時に実行される開始されていないスレッドを登録できます。このコードは、クリーンアップ自体を実行したり、実行中のデーモン スレッドに作業を終了するよう通知したりできます。多くのシステムでは、プログラムが強制終了される前にクリーンアップを行う時間が限られているため、このようなクリーンアップ コードは比較的高速である必要があります。

おそらく、バックグラウンド スレッドをデーモン スレッドにすることも検討できます。その後、終了時に JVM をブロックせずmain、クリーンアップ フェーズ中も実行されます。

SIGKILL をインターセプトできないことに注意してください。このシグナルは、避けられず、即座に送信されるように設計されています。ただし、SIGTERM、SIGHUP、および同様のシグナルで動作するはずです。


更新:ExecutorServiceデーモン スレッドを実行するを簡単に作成できます。必要なのは、適切な を作成することだけですThreadFactory:

public static class DaemonFactory
        implements ThreadFactory
    {
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            t.setDaemon(true);
            return t;
        }
    }

いいねを作成するよりExecutorService

public static void main(String argv[])
    throws Exception
{
    ExecutorService es 
        = Executors.newCachedThreadPool(new DaemonFactory());
    //                                  ^^^^^^^^^^^^^^^^^^^
    es.submit(new Callable<Object>() {
        public Object call() throws Exception {
            Thread.sleep(100);
            System.err.println("Daemon: " +
                Thread.currentThread().isDaemon());
            return null;
        }
    });
    // Without this, JVM will terminate before the daemon thread prints the
    // message, because JVM doesn't wait for daemon threads when
    // terminating:
    es.awaitTermination(3, TimeUnit.SECONDS);
}

に関してThread.join()は、 によって管理されるスレッドで使用しようとしないでくださいExecutorService。それらを管理するのは実行者の責任です。スレッドを列挙する信頼できる方法はありません。エグゼキュータは、その構成などに応じてスレッドを作成および破棄できます。唯一の信頼できる方法はshutdown();、次に呼び出すことawaitTermination(...);です。

于 2013-07-04T14:45:41.870 に答える