ScheduledExecutorServiceを使用して、メソッドを定期的に実行します。
pコード:
_ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<?> handle =
scheduler.scheduleWithFixedDelay(new Runnable() {
public void run() {
//Do business logic, may Exception occurs
}
}, 1, 10, TimeUnit.SECONDS);
_
私の質問:
run()
が例外をスローする場合、スケジューラーを続行する方法は?メソッドrun()
のすべての例外をキャッチする必要がありますか?または、例外を処理するための組み込みコールバックメソッドはありますか?ありがとう!
scheduler.scheduleWithFixedDelay(...)
によって返されるScheduledFuture
オブジェクトを次のように使用する必要があります。
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<?> handle =
scheduler.scheduleWithFixedDelay(new Runnable() {
public void run() {
throw new RuntimeException("foo");
}
}, 1, 10, TimeUnit.SECONDS);
// Create and Start an exception handler thread
// pass the "handle" object to the thread
// Inside the handler thread do :
....
try {
handle.get();
} catch (ExecutionException e) {
Exception rootException = e.getCause();
}
run
メソッドをエスケープする例外は、予告なしにすべての作業を停止します。
常にrun
メソッド内で_try-catch
_を使用してください。スケジュールされたアクティビティを続行する場合は、回復を試みてください。
_@Override
public void run ()
{
try {
doChore();
} catch ( Exception e ) {
logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + t.getStackTrace() );
}
}
_
質問は、 ScheduledExecutorService
:を使用した重要なトリックに関するものです。スローされた例外またはエラーがエグゼキューターに到達すると、エグゼキューターが停止します。Runnableでの呼び出しはもう必要ありません。この作業停止は黙って行われ、通知されません。このいたずらな言語 ブログ投稿 は、この振る舞いについて学ぶための難しい方法を楽しく語っています。
yegor256による回答 と arun_sureshによる回答 は両方とも基本的に正しいようです。それらの答えに関する2つの問題:
Java通常キャッチするのは 例外 であり、 エラー ではありません。ただし、ScheduledExecutorServiceのこの特殊なケースでは、どちらかをキャッチできなければ作業を意味します停止しますので、両方をキャッチすることもできますが、すべてのエラーをキャッチすることの意味を完全に理解していないため、これについては100%確信が持てません。
例外とエラーの両方をキャッチする1つの方法は、スーパークラス Throwable をキャッチすることです。
_} catch ( Throwable t ) {
_
…のではなく…
_} catch ( Exception e ) {
_
Try-Catch
_を追加するだけですしかし、両方の答えは少し複雑です。記録のために、最も簡単な解決策を示します。
Runnableのコードを常にTry-Catchにラップして、すべての例外andエラーをキャッチします。
ラムダ付き(Java 8以降)。
_final Runnable someChoreRunnable = () -> {
try {
doChore();
} catch ( Throwable t ) { // Catch Throwable rather than Exception (a subclass).
logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + t.getStackTrace() );
}
};
_
ラムダ以前の昔ながらの方法。
_final Runnable someChoreRunnable = new Runnable()
{
@Override
public void run ()
{
try {
doChore();
} catch ( Throwable t ) { // Catch Throwable rather than Exception (a subclass).
logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + t.getStackTrace() );
}
}
};
_
ScheduledExecutorService
に関係なく、anyで一般的なtry-catch( Exception† e )
を常に使用するのが賢明なようです- run
Runnable
のメソッド。 call
のメソッド Callable
についても同様です。
実際の作業では、Runnable
をネストではなく個別に定義する可能性があります。しかし、これはきちんとしたオールインワンの例になります。
_package com.basilbourque.example;
import Java.time.ZoneId;
import Java.time.ZonedDateTime;
import Java.util.concurrent.Executors;
import Java.util.concurrent.ScheduledExecutorService;
import Java.util.concurrent.ScheduledFuture;
import Java.util.concurrent.TimeUnit;
/**
* Demo `ScheduledExecutorService`
*/
public class App {
public static void main ( String[] args ) {
App app = new App();
app.doIt();
}
private void doIt () {
// Demonstrate a working scheduled executor service.
// Run, and watch the console for 20 seconds.
System.out.println( "BASIL - Start." );
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture < ? > handle =
scheduler.scheduleWithFixedDelay( new Runnable() {
public void run () {
try {
// doChore ; // Do business logic.
System.out.println( "Now: " + ZonedDateTime.now( ZoneId.systemDefault() ) ); // Report current moment.
} catch ( Exception e ) {
// … handle exception/error. Trap any unexpected exception here rather to stop it reaching and shutting-down the scheduled executor service.
// logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + e.getStackTrace() );
} // End of try-catch.
} // End of `run` method.
} , 0 , 2 , TimeUnit.SECONDS );
// Wait a long moment, for background thread to do some work.
try {
Thread.sleep( TimeUnit.SECONDS.toMillis( 20 ) );
} catch ( InterruptedException e ) {
e.printStackTrace();
}
// Time is up. Kill the executor service and its thread pool.
scheduler.shutdown();
System.out.println( "BASIL - Done." );
}
}
_
実行するとき。
BASIL-開始。
現在:2018-04-10T16:46:01.423286-07:00 [America/Los_Angeles]
現在:2018-04-10T16:46:03.449178-07:00 [America/Los_Angeles]
現在:2018-04-10T16:46:05.450107-07:00 [America/Los_Angeles]
現在:2018-04-10T16:46:07.450586-07:00 [アメリカ/ Los_Angeles]
現在:2018-04-10T16:46:09.456076-07:00 [America/Los_Angeles]
現在:2018-04-10T16:46:11.456872-07:00 [アメリカ/ Los_Angeles]
現在:2018-04-10T16:46:13.461944-07:00 [アメリカ/ Los_Angeles]
現在:2018-04-10T16:46:15.463837-07:00 [アメリカ/ロスアンジェレス]
現在:2018-04-10T16:46:17.469218-07:00 [America/Los_Angeles]
現在:2018-04-10T16:46:19.473935-07:00 [America/Los_Angeles]
BASIL-完了。
†または、おそらく Throwable
の代わりに Exception
を使用して Error
オブジェクトもキャッチします。
@MBecソリューションに触発されて、ScheduledExecutorServiceのNice汎用ラッパーを作成しました。
:)
import Java.util.List;
import Java.util.concurrent.Callable;
import Java.util.concurrent.CompletableFuture;
import Java.util.concurrent.ScheduledExecutorService;
import Java.util.concurrent.TimeUnit;
/**
* This class use as a wrapper for the Native Java ScheduledExecutorService class.
* It was created in order to address the very unpleasant scenario of silent death!
* explanation: each time an unhandled exception get thrown from a running task that runs by ScheduledExecutorService
* the thread will die and the exception will die with it (nothing will propagate back to the main thread).
*
* However, HonestScheduledExecutorService will gracefully print the thrown exception with a custom/default message,
* and will also return a Java 8 compliant CompletableFuture for your convenience :)
*/
@Slf4j
public class HonestScheduledExecutorService {
private final ScheduledExecutorService scheduledExecutorService;
private static final String DEFAULT_FAILURE_MSG = "Failure occurred when running scheduled task.";
HonestScheduledExecutorService(ScheduledExecutorService scheduledExecutorService) {
this.scheduledExecutorService = scheduledExecutorService;
}
public CompletableFuture<Object> scheduleWithFixedDelay(Callable callable, String onFailureMsg, long initialDelay, long delay, TimeUnit unit) {
final String msg = StringUtils.isEmpty(onFailureMsg) ? DEFAULT_FAILURE_MSG : onFailureMsg;
CompletableFuture<Object> delayed = new CompletableFuture<>();
scheduledExecutorService.scheduleWithFixedDelay(() -> {
try {
Object result = callable.call();
delayed.complete(result);
} catch (Throwable th) {
log.error(msg, th);
delayed.completeExceptionally(th);
}
}, initialDelay, delay, unit);
return delayed;
}
public CompletableFuture<Void> scheduleWithFixedDelay(Runnable runnable, String onFailureMsg, long initialDelay, long delay, TimeUnit unit) {
final String msg = StringUtils.isEmpty(onFailureMsg) ? DEFAULT_FAILURE_MSG : onFailureMsg;
CompletableFuture<Void> delayed = new CompletableFuture<>();
scheduledExecutorService.scheduleWithFixedDelay(() -> {
try {
runnable.run();
delayed.complete(null);
} catch (Throwable th) {
log.error(msg, th);
delayed.completeExceptionally(th);
}
}, initialDelay, delay, unit);
return delayed;
}
public CompletableFuture<Object> schedule(Callable callable, String failureMsg, long delay, TimeUnit unit) {
final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg;
CompletableFuture<Object> delayed = new CompletableFuture<>();
scheduledExecutorService.schedule(() -> {
try {
Object result = callable.call();
delayed.complete(result);
} catch (Throwable th) {
log.error(msg, th);
delayed.completeExceptionally(th);
}
}, delay, unit);
return delayed;
}
public CompletableFuture<Void> schedule(Runnable runnable, String failureMsg, long delay, TimeUnit unit) {
final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg;
CompletableFuture<Void> delayed = new CompletableFuture<>();
scheduledExecutorService.schedule(() -> {
try {
runnable.run();
delayed.complete(null);
} catch (Throwable th) {
log.error(msg, th);
delayed.completeExceptionally(th);
}
}, delay, unit);
return delayed;
}
public CompletableFuture<Object> scheduleAtFixedRate(Callable callable, String failureMsg, long initialDelay, long period, TimeUnit unit) {
final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg;
CompletableFuture<Object> delayed = new CompletableFuture<>();
scheduledExecutorService.scheduleAtFixedRate(() -> {
try {
Object result = callable.call();
delayed.complete(result);
} catch (Throwable th) {
log.error(msg, th);
delayed.completeExceptionally(th);
}
}, initialDelay, period, unit);
return delayed;
}
public CompletableFuture<Void> scheduleAtFixedRate(Runnable runnable, String failureMsg, long initialDelay, long period, TimeUnit unit) {
final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg;
CompletableFuture<Void> delayed = new CompletableFuture<>();
scheduledExecutorService.scheduleAtFixedRate(() -> {
try {
runnable.run();
delayed.complete(null);
} catch (Throwable th) {
log.error(msg, th);
delayed.completeExceptionally(th);
}
}, initialDelay, period, unit);
return delayed;
}
public CompletableFuture<Object> execute(Callable callable, String failureMsg) {
final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg;
CompletableFuture<Object> delayed = new CompletableFuture<>();
scheduledExecutorService.execute(() -> {
try {
Object result = callable.call();
delayed.complete(result);
} catch (Throwable th) {
log.error(msg, th);
delayed.completeExceptionally(th);
}
});
return delayed;
}
public CompletableFuture<Void> execute(Runnable runnable, String failureMsg) {
final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg;
CompletableFuture<Void> delayed = new CompletableFuture<>();
scheduledExecutorService.execute(() -> {
try {
runnable.run();
delayed.complete(null);
} catch (Throwable th) {
log.error(msg, th);
delayed.completeExceptionally(th);
}
});
return delayed;
}
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
return scheduledExecutorService.awaitTermination(timeout, unit);
}
public List<Runnable> shutdownNow() {
return scheduledExecutorService.shutdownNow();
}
public void shutdown() {
scheduledExecutorService.shutdown();
}
}
これは古い質問であることは知っていますが、誰かが遅延CompletableFuture
をScheduledExecutorService
とともに使用している場合は、この方法で処理する必要があります。
private static CompletableFuture<String> delayed(Duration delay) {
CompletableFuture<String> delayed = new CompletableFuture<>();
executor.schedule(() -> {
String value = null;
try {
value = mayThrowExceptionOrValue();
} catch (Throwable ex) {
delayed.completeExceptionally(ex);
}
if (!delayed.isCompletedExceptionally()) {
delayed.complete(value);
}
}, delay.toMillis(), TimeUnit.MILLISECONDS);
return delayed;
}
CompletableFuture
の例外の処理:
CompletableFuture<String> delayed = delayed(Duration.ofSeconds(5));
delayed.exceptionally(ex -> {
//handle exception
return null;
}).thenAccept(value -> {
//handle value
});
別の解決策は、Runnable
の例外を飲み込むことです。 jcabi-log の便利な VerboseRunnable
クラスを使用できます。例:
import com.jcabi.log.VerboseRunnable;
scheduler.scheduleWithFixedDelay(
new VerboseRunnable(
Runnable() {
public void run() {
// do business logic, may Exception occurs
}
},
true // it means that all exceptions will be swallowed and logged
),
1, 10, TimeUnit.SECONDS
);
古い質問ですが、受け入れられた答えは説明を与えず、貧しい例を提供し、最も賛成の答えはいくつかの点で正しいですが、最後にすべてのRunnable.run()
メソッドにcatch
例外を追加することをお勧めします。
同意しない理由:
例外の伝播はExecutorService
フレームワークによって実行されるべきであり、実際にその機能を提供すると思います。
それ以外に、ExecutorService
の動作方法を短絡させてあまりにも賢くしようとするのも良い考えではありません。フレームワークは進化する可能性があり、標準的な方法で使用したい場合があります。
最後に、ExecutorService
フレームワークにジョブを実行させることは、必ずしも後続の呼び出しタスクを停止することを意味しません。
スケジュールされたタスクで問題が発生した場合、それは問題の原因に応じてタスクを再スケジュールするかしないかの呼び出し側の責任です。
各層にはそれぞれの責任があります。これらを維持することで、コードを明確かつ保守しやすくします。
ScheduledExecutorService.scheduleWithFixedDelay()/scheduleAtFixRate()
仕様の状態:
タスクの実行で例外が発生した場合、以降の実行は抑制されます。そうでない場合、タスクは、エグゼキューターのキャンセルまたは終了によってのみ終了します。
つまり、ScheduledFuture.get()
はスケジュールされた呼び出しごとに返されませんが、タスクの最後の呼び出しに対して返されます。つまり、ScheduledFuture.cancel()
またはタスクでスローされた例外が原因でタスクがキャンセルされます。 。
したがって、ScheduledFuture
リターンを処理して、ScheduledFuture.get()
で例外をキャプチャすると、正しく見えます。
try {
future.get();
} catch (InterruptedException e) {
// ... to handle
} catch (ExecutionException e) {
// ... and unwrap the exception OR the error that caused the issue
Throwable cause = e.getCause();
}
3回目の実行で例外をスローし、スケジューリングを終了するタスクを実行します。いくつかのシナリオでは、それが必要です。
import Java.util.concurrent.ExecutionException;
import Java.util.concurrent.Executors;
import Java.util.concurrent.Future;
import Java.util.concurrent.ScheduledExecutorService;
import Java.util.concurrent.TimeUnit;
import Java.util.concurrent.atomic.AtomicInteger;
public class ScheduledExecutorServiceWithException {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
// variable used to thrown an error at the 3rd task invocation
AtomicInteger countBeforeError = new AtomicInteger(3);
// boolean allowing to leave the client to halt the scheduling task or not after a failure
Future<?> futureA = executor
.scheduleWithFixedDelay(new MyRunnable(countBeforeError), 1, 2, TimeUnit.SECONDS);
try {
System.out.println("before get()");
futureA.get(); // will return only if canceled
System.out.println("after get()");
} catch (InterruptedException e) {
// handle that : halt or no
} catch (ExecutionException e) {
System.out.println("exception caught :" + e.getCause());
}
// shutdown the executorservice
executor.shutdown();
}
private static class MyRunnable implements Runnable {
private final AtomicInteger invocationDone;
public MyRunnable(AtomicInteger invocationDone) {
this.invocationDone = invocationDone;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ", execution");
if (invocationDone.decrementAndGet() == 0) {
throw new IllegalArgumentException("ohhh an Exception in MyRunnable");
}
}
}
}
出力:
get() pool-1-thread-1の前、execution pool-1-thread-1、execution pool-1-thread-1 execution exception catch:Java.lang.IllegalArgumentException:ohhh an Exception in MyRunnable
最初の2回の実行で例外をスローし、3回目の実行でエラーをスローするタスクを実行します。タスクのクライアントがスケジューリングを停止するかしないかを選択できることがわかります。ここでは例外の場合に進み、エラーの場合に停止します。
import Java.util.concurrent.ExecutionException;
import Java.util.concurrent.Executors;
import Java.util.concurrent.Future;
import Java.util.concurrent.ScheduledExecutorService;
import Java.util.concurrent.TimeUnit;
import Java.util.concurrent.atomic.AtomicInteger;
public class ScheduledExecutorServiceWithException {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
// variable used to thrown an error at the 3rd task invocation
AtomicInteger countBeforeError = new AtomicInteger(3);
// boolean allowing to leave the client to halt the scheduling task or not after a failure
boolean mustHalt = true;
do {
Future<?> futureA = executor
.scheduleWithFixedDelay(new MyRunnable(countBeforeError), 1, 2, TimeUnit.SECONDS);
try {
futureA.get(); // will return only if canceled
} catch (InterruptedException e) {
// handle that : halt or not halt
} catch (ExecutionException e) {
if (e.getCause() instanceof Error) {
System.out.println("I halt in case of Error");
mustHalt = true;
} else {
System.out.println("I reschedule in case of Exception");
mustHalt = false;
}
}
}
while (!mustHalt);
// shutdown the executorservice
executor.shutdown();
}
private static class MyRunnable implements Runnable {
private final AtomicInteger invocationDone;
public MyRunnable(AtomicInteger invocationDone) {
this.invocationDone = invocationDone;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ", execution");
if (invocationDone.decrementAndGet() == 0) {
throw new Error("ohhh an Error in MyRunnable");
} else {
throw new IllegalArgumentException("ohhh an Exception in MyRunnable");
}
}
}
}
出力:
pool-1-thread-1、実行 例外の場合に再スケジュール pool-1-thread-1、実行 例外の場合に再スケジュール pool-1-thread-2、実行 エラーの場合に停止します
(ScheduledExecutorService)に渡されるスレッドのrun()の例外はスローされず、future.get()を使用してステータスを取得すると、メインスレッドは無限に待機します。