22

ScheduledExecutorServiceで実行しているときに、タスク自体の内部からタスクの繰り返しを停止するための優れた方法はありますか?

たとえば、次のタスクがあります。

Future<?> f = scheduledExecutor.scheduleAtFixedRate(new Runnable() {
    int count = 0;
    public void run() {
       System.out.println(count++);
       if (count == 10) {
           // ??? cancel self
       }
    }
}, 1, 1, TimeUnit.SECONDS);

外部からはf.cancel()で簡単にキャンセルできますが、指定した場所で繰り返しを止めるにはどうすればよいですか?(AtomicReferenceを介してFutureを渡すことは安全ではありません。これは、scheduleAtFixedRateがf lateを返し、変数もlateに設定され、タスク自体がすでに実行されている可能性があり、参照にnullが含まれている可能性があるためです。)

4

5 に答える 5

14

繰り返しタスクが例外またはエラーをスローすると、それはFutureに配置され、タスクは再度繰り返されません。選択したRuntimeExceptionまたはErrorをスローできます。

于 2011-02-05T21:47:03.117 に答える
5

匿名の内部クラスを使用する代わりに、タスクをスケジュールするときにFuture取得するオブジェクトのプロパティを持つことができる名前付きクラスを使用できます。Executor

abstract class FutureRunnable implements Runnable {

    private Future<?> future;

    /* Getter and Setter for future */

}

タスクをスケジュールするときに、をに渡すことができFutureますRunnable

FutureRunnable runnable = new FutureRunnable() {

    public void run() {
        if (/* abort condition */)
            getFuture().cancel(false);
    }

};
Future<?> future = executor.scheduleAtFixedRate(runnable, ...);
runnable.setFuture(future);

が設定される前にタスクが実行されないことを確認する必要があるかもしれません。Futureそうしないと、が取得されるためNullPointerExceptionです。

于 2011-02-05T23:59:02.417 に答える
2

Runnableが実行中のエグゼキュータについて何かを知っている、または10に到達してもエラー状態でない場合にエラーをスローすることは、ハックであるため、Runnableにとって悪い設計のようです。

スケジューリングと実行の外で10へのループを実行できますか?これには、自分で手動でスケジュールするため、スケジュールされていないエグゼキュータを使用する必要がある場合があります。

于 2011-02-05T22:03:05.043 に答える
1

これは別の方法です。それはスレッドセーフですらあります。

    final Future<?>[] f = {null};
    f[0]=  scheduledExecutor.scheduleAtFixedRate(new Runnable() {
        int count = 0;
        public void run() {

            System.out.println(count++);
            if (count == 10) {
                Future<?> future;
                while(null==(future = f[0])) Thread.yield();//prevent exceptionally bad thread scheduling 
                future.cancel(false);
                return;
                //cancel self
            }
        }
    }, 1, 1, TimeUnit.SECONDS);
于 2011-02-06T00:24:05.593 に答える
1

これを見たばかりです...同じことをしたかったので...これが私の解決策です。これはスレッドセーフだと思います。

まず、Futureのコンテナを作成します。

public static class Cancel {
    private ScheduledFuture<?> future;

    public synchronized void setFuture(ScheduledFuture<?> future) {
        this.future = future;
    }

    public synchronized void stop() {
        LOG.debug("cancelling {}", future);
        future.cancel(false);
    }
}

そして、将来のコード:

    final Cancel controller = new Cancel();

    synchronized (controller) {
        ScheduledFuture<?> future = scheduler.scheduleWithFixedDelay(() -> {
            if (<CONTINUE RUNNING CONDITION) {

            } else {
                // STOP SCHEDULABLE FUTURE
                controller.stop();
            }
        }, startTime, timeBetweenVisbilityChecks);
        controller.setFuture(future);
    }
}

したがって、futureが作成され、コントローラーにfutureが設定されるまで、ストップが呼び出されないことに注意してください。

Runnableは匿名の内部クラスであり、これはまったく別のスレッドで実行されることに注意してください。

于 2015-04-30T10:56:08.507 に答える