Try-catch是用来帮助异常处理的。这意味着它将在某种程度上帮助我们的系统变得更加健壮:尝试从意外事件中恢复。

我们怀疑在执行和指令(发送消息)时可能会发生一些事情,因此它被包含在try中。如果发生了几乎意想不到的事情,我们可以做些什么:我们编写捕获。我不认为我们调用只是为了记录异常。我认为catch块是为了给我们从错误中恢复的机会。

现在,假设我们从错误中恢复,因为我们可以修复错误。如果能再试一次,那就太好了:

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

这将很快陷入永恒循环,但让我们假设fix_the_problem返回true,然后重试。假设在Java中没有这样的东西,你将如何解决这个问题?你认为解决这个问题的最佳设计代码是什么?

这就像一个哲学问题,因为我已经知道Java不直接支持我所要求的内容。


当前回答

这里是我的解决方案类似于其他人可以包装一个函数,但允许您获得函数返回值,如果它成功。

    /**
     * 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");
    }

其他回答

使用带有本地状态标志的while循环。初始化标志为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次,如果不成功,直到那时将退出,如果它成功之前。

此解决方案允许您配置一个可重用的功能,以便在不使用任何外部库的情况下基于特定异常进行重试

//创建一个适合你需要的函数。

@FunctionalInterface
public interface ThrowableBiFunction<U,T,R> {
    R apply(U u ,T t) throws Exception;
}

//这是解决方案的关键

public interface ExceptionRetryable<T, U, R> {
    
 int getRetries();
   
 List<Class<? extends Exception>> getRetryableExceptions();

    default R execute(ThrowableBiFunction<T, U, R> function, T t, U u) throws Exception {
        int numberOfRetries = getRetries();
        return execute(function, t, u, numberOfRetries);
    }

    default R execute(ThrowableBiFunction<T, U, R> function, T t, U u, int retryCount) throws Exception {
        try {
            log.info(" Attempting to execute ExceptionRetryable#execute ,Number of remaining retries {} ",retryCount);
            return function.apply(t, u);
        } catch (Exception e) {

           log.info(" error occurred in ExceptionRetryable#execute",e);
            if (retryCount == 0)
                throw e;
            for (Class exp : getRetryableExceptions()) {
                if (e.getClass() == exp) {
                   return execute(function, t, u, retryCount - 1);
                }
            }
            throw e;
        }

    }
}

//创建一个异常可重执行的实现

public class TestRetryable implements ExceptionRetryable<String, String, List<String>> {
    @Override
    public int getRetries() {
        return 10;
    }

    @Override
    public List<Class<? extends Exception>> getRetryableExceptions() {
        return Arrays.asList(new Exception1().getClass(), new Exception2().getClass());
        ;
    }
}

//最后创建一个throwablebiffunction,它封装了需要在exception上重试的代码段和ExceptionRetryable实例

 TestRetryable retryable = new TestRetryable();
    ThrowableBiFunction<Integer,Long, String> testRetrablefcn = { i, l ->
           // your code goes here
};
    Integer i = 0;
    Long l = 1l;
      String output = testRetrablefcn.execute(testRetrablefcn,i,l); 

下面是Java 8+的可重用和更通用的方法,不需要外部库:

public interface IUnreliable<T extends Exception>
{
    void tryRun ( ) throws T;
}

public static <T extends Exception> void retry (int retryCount, IUnreliable<T> runnable) throws T {
    for (int retries = 0;; retries++) {
        try {
            runnable.tryRun();
            return;
        } catch (Exception e) {
            if (retries < retryCount) {
                continue;
            } else {
                throw e;
            }
        }
    }
}

用法:

@Test
public void demo() throws IOException {
    retry(3, () -> {
        new File("/tmp/test.txt").createNewFile();
    });
}

https://onlinegdb.com/a-7RsL1Gh

    public void doSomething() throws Exception{
      final int MAX_TRIES = 10;
      int count = 0;
      
      while(count++ < MAX_TRIES){
         try{ 
            System.out.println("trying");
            causeIssue(count); // throws error/exception till count 2
            System.out.println("trying successful");
            break; // break on success
         } catch (Exception e){
           System.out.println("caught, logging Exception:" + count);
         } catch (Error e){
           System.out.println("caught, logging Error:" + count);
         }
      }
    }

输出:

trying
caught, logging Error:1
trying
caught, logging Error:2
trying
trying successful

这里是我的解决方案类似于其他人可以包装一个函数,但允许您获得函数返回值,如果它成功。

    /**
     * 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");
    }