243

Try-catch は、例外処理を支援するためのものです。これはどういうわけか、システムをより堅牢にするのに役立つことを意味します。予期しないイベントからの回復を試みてください。

命令の実行時(メッセージの送信時)に何かが起こる可能性があると思われるので、try で囲みます。ほとんど予想外のことが起こった場合、私たちは何かをすることができます: キャッチを書きます。例外をログに記録するためだけに呼び出したとは思いません。catch ブロックは、エラーから回復する機会を与えるためのものだと思います。

さて、何が間違っていたのかを修正できたので、エラーから回復したとしましょう。再試行するのはとてもいいことです:

try{ some_instruction(); }
catch (NearlyUnexpectedException e){
   fix_the_problem();
   retry;
}

これはすぐに永遠のループに陥りますが、fix_the_problem が true を返すとしましょう。その後、再試行します。Java にはそのようなものがないことを考えると、この問題をどのように解決しますか? これを解決するための最適な設計コードは何ですか?

これは、私が求めているものが Java によって直接サポートされていないことを既に知っていることを考えると、哲学的な質問のようなものです。

4

28 に答える 28

378

try-catch次のように内側をwhileループで囲む必要があります: -

int count = 0;
int maxTries = 3;
while(true) {
    try {
        // Some Code
        // break out of loop, or return, on success
    } catch (SomeException e) {
        // handle exception
        if (++count == maxTries) throw e;
    }
}

で例外が発生し続ける場合に備えて、無限ループcountmaxTries陥らないようにしましたtry block

于 2012-11-05T20:39:22.747 に答える
68

必須の「エンタープライズ」ソリューション:

public abstract class Operation {
    abstract public void doIt();
    public void handleException(Exception cause) {
        //default impl: do nothing, log the exception, etc.
    }
}

public class OperationHelper {
    public static void doWithRetry(int maxAttempts, Operation operation) {
        for (int count = 0; count < maxAttempts; count++) {
            try {
                operation.doIt();
                count = maxAttempts; //don't retry
            } catch (Exception e) {
                operation.handleException(e);
            }
        }
    }
}

そして呼び出すには:

OperationHelper.doWithRetry(5, new Operation() {
    @Override public void doIt() {
        //do some stuff
    }
    @Override public void handleException(Exception cause) {
        //recover from the Exception
    }
});
于 2012-11-05T21:21:10.587 に答える
41

いつものように、最適な設計は特定の状況によって異なります。通常、私は次のように書きます。

for (int retries = 0;; retries++) {
    try {
        return doSomething();
    } catch (SomeException e) {
        if (retries < 6) {
            continue;
        } else {
            throw e;
        }
    }
}
于 2012-11-05T20:39:56.787 に答える
25

Failsafeを介して処理される正確なシナリオ:

RetryPolicy retryPolicy = new RetryPolicy()
  .retryOn(NearlyUnexpectedException.class);

Failsafe.with(retryPolicy)
  .onRetry((r, f) -> fix_the_problem())
  .run(() -> some_instruction());

ものすごく単純。

于 2016-04-12T20:02:45.853 に答える
19

try/catchintoはよく知られたwhile優れた戦略ですが、再帰呼び出しをお勧めします。

void retry(int i, int limit) {
    try {

    } catch (SomeException e) {
        // handle exception
        if (i >= limit) {
            throw e;  // variant: wrap the exception, e.g. throw new RuntimeException(e);
        }
        retry(i++, limit);
    }
}
于 2012-11-05T20:44:15.167 に答える
19

jcabi-aspectsから AOP および Java アノテーションを使用できます(私は開発者です)。

@RetryOnFailure(attempts = 3, delay = 5)
public String load(URL url) {
  return url.openConnection().getContent();
}

@Loggableおよび@LogException注釈を使用することもできます。

于 2013-03-24T07:14:22.497 に答える
6

これらの答えのほとんどは本質的に同じです。私もそうですが、この形が好きです

boolean completed = false;
Throwable lastException = null;
for (int tryCount=0; tryCount < config.MAX_SOME_OPERATION_RETRIES; tryCount++)
{
    try {
        completed = some_operation();
        break;
    }
    catch (UnlikelyException e) {
        lastException = e;
        fix_the_problem();
    }
}
if (!completed) {
    reportError(lastException);
}
于 2012-11-05T20:49:58.127 に答える
5

ローカルフラグでwhileループを使用します。statusフラグを次のように初期化し、操作が成功したときに次のfalseように設定しtrueます。

  boolean success  = false;
  while(!success){
     try{ 
         some_instruction(); 
         success = true;
     } catch (NearlyUnexpectedException e){
       fix_the_problem();
     }
  }

これは、成功するまで再試行を続けます。

特定の回数だけ再試行する場合は、カウンターも使用します。

  boolean success  = false;
  int count = 0, MAX_TRIES = 10;
  while(!success && count++ < MAX_TRIES){
     try{ 
         some_instruction(); 
         success = true;
     } catch (NearlyUnexpectedException e){
       fix_the_problem();
     }
  }
  if(!success){
    //It wasn't successful after 10 retries
  }

これは、成功しない場合は最大 10 回試行し、事前に成功した場合は終了します。

于 2012-11-05T20:39:39.610 に答える
2

この問題を解決する簡単な方法は、try/catch を while ループでラップし、カウントを維持することです。このようにして、失敗のログを維持しながら、他の変数に対してカウントをチェックすることで、無限ループを防ぐことができます。これは最も洗練されたソリューションではありませんが、機能します。

于 2012-11-05T20:40:12.777 に答える
1

Try-Catch が行うことは、プログラムが正常に失敗することを許可することだけです。通常、catch ステートメントでは、エラーをログに記録し、必要に応じて変更をロールバックします。

bool finished = false;

while(finished == false)
{
    try
    {
        //your code here
        finished = true
    }
    catch(exception ex)
    {
        log.error("there was an error, ex");
    }
}
于 2012-11-05T20:40:16.817 に答える
1

do-while を使用して再試行ブロックを設計します。

boolean successful = false;
int maxTries = 3;
do{
  try {
    something();
    success = true;
  } catch(Me ifUCan) {
    maxTries--;
  }
} while (!successful || maxTries > 0)
于 2014-07-28T07:46:04.037 に答える
0

これが「プロフェッショナル」な方法であるかどうかはわかりません。また、それがすべてで機能するかどうかも完全にはわかりません.

boolean gotError = false;

do {
    try {
        // Code You're Trying
    } catch ( FileNotFoundException ex ) {
        // Exception
        gotError = true;
    }
} while ( gotError = true );
于 2015-09-04T01:24:45.710 に答える
0

ここにはすでに多くの同様の回答があることを知っていますが、私のものはそれほど違いはありませんが、特定のケース/問題を扱っているため、とにかく投稿します。

facebook Graph APIを処理するときPHPにエラーが発生することがありますが、すぐに同じことを再試行すると、肯定的な結果が得られます (この質問の範囲を超えているさまざまな魔法のインターネットの理由による)。この場合、エラーを修正する必要はありませんが、何らかの「facebook エラー」があったため、単純に再試行してください。

このコードは、Facebook セッションを作成した直後に使用されます。

//try more than once because sometimes "facebook error"
$attempt = 3;
while($attempt-- > 0)
{
    // To validate the session:
    try 
    {
        $facebook_session->validate();
        $attempt = 0;
    } 
    catch (Facebook\FacebookRequestException $ex)
    {
        // Session not valid, Graph API returned an exception with the reason.
        if($attempt <= 0){ echo $ex->getMessage(); }
    } 
    catch (\Exception $ex) 
    {
        // Graph API returned info, but it may mismatch the current app or have expired.
        if($attempt <= 0){ echo $ex->getMessage(); }
    }
}

また、forループ カウントをゼロ ( $attempt--) にすることで、将来の試行回数を簡単に変更できます。

于 2014-08-14T15:19:40.210 に答える
0

これは、関数をラップできる他のいくつかのソリューションと同様の私のソリューションですが、成功した場合は関数の戻り値を取得できます。

    /**
     * Wraps a function with retry logic allowing exceptions to be caught and retires made.
     *
     * @param function the function to retry
     * @param maxRetries maximum number of retires before failing
     * @param delay time to wait between each retry
     * @param allowedExceptionTypes exception types where if caught a retry will be performed
     * @param <V> return type of the function
     * @return the value returned by the function if successful
     * @throws Exception Either an unexpected exception from the function or a {@link RuntimeException} if maxRetries is exceeded
     */
    @SafeVarargs
    public static <V> V runWithRetriesAndDelay(Callable<V> function, int maxRetries, Duration delay, Class<? extends Exception>... allowedExceptionTypes) throws Exception {
        final Set<Class<? extends Exception>> exceptions = new HashSet<>(Arrays.asList(allowedExceptionTypes));
        for(int i = 1; i <= maxRetries; i++) {
            try {
                return function.call();
            } catch (Exception e) {
                if(exceptions.contains(e.getClass())){
                    // An exception of an expected type
                    System.out.println("Attempt [" + i + "/" + maxRetries + "] Caught exception [" + e.getClass() + "]");
                    // Pause for the delay time
                    Thread.sleep(delay.toMillis());
                }else {
                    // An unexpected exception type
                    throw e;
                }
            }
        }
        throw new RuntimeException(maxRetries + " retries exceeded");
    }
于 2020-05-29T10:58:09.730 に答える