如果发生异常,如何停止执行程序服务中的可调用任务
How to stop Callable tasks in Executor service if exception occur
我正在尝试实现示例应用程序来测试 Callable 和 ExecutorService 接口。
在我的应用程序中我有:
@Bean("fixedThreadPool")
public ExecutorService fixedThreadPool() {
return Executors.newFixedThreadPool(5);
}
然后:
public void removeUserIds(Set<String> userIds) {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://localhost:8080/remove");
final List<Callable<String>> callables = new ArrayList<>();
userIds.forEach(userId -> {
final Callable<String> task = () -> callServiceToRemove(builder,userId); //Call to remote service using RestTemplate
callables.add(task);
});
try {
final List<Future<String>> futureList =
executor.invokeAll(callables);
futureList.forEach(future -> {
try {
log.info(future.get());
} catch (final Exception e) {
log.error("Error : "+ e.getMessage());
}
});
} catch (final Exception e) {
log.error("Error Error Error : "+ e.getMessage());
} finally {
executor.shutdown();
}
}
当我使用 100 个 userIds 调用 removeUserIds() 方法时,它在快乐流程中工作正常,但如果服务不可用或关闭,第 100 次打印错误。如果服务不可用或关闭,我无法 stop/terminate 线程,因此服务不会发生进一步的调用。
任何人都可以在这里帮助解决这个问题,如果服务中断,我如何停止线程执行,或者在这里提出可行的解决方案?
将 callServiceToRemove 调用移到 try catch 块中。
在 catch 块中你可以使用 executor.shutdownNow();
shutdownNow() 尝试停止所有正在执行的任务,停止等待任务的处理,以及 returns 等待执行的任务列表。
此方法不会等待主动执行的任务终止并尝试强制停止它们。除了尽最大努力停止处理正在执行的任务之外,没有任何保证。此实现通过 Thread.interrupt() 取消任务,因此任何未能响应中断的任务可能永远不会终止。
与其说是编码问题,不如说这是设计问题。可能有几种方法。您可以以此为例:
使用全局标志
在实际触发远程服务调用之前寻找全局布尔标志,例如 Globals.serviceUnavailable
。 这个全局标志可以由遇到远程错误的第一个服务设置。这里是对代码的更改。
final Callable<String> task = () -> {
try{
if( !Globals.serviceUnavailable ) callServiceToRemove(builder,userId);
}
catch( ServiceUnavailableException e ){ //Or whatever your exception is
Globals.serviceUnavailable = true; //Notify the remaining tasks that the service is unavailable.
}
}
当然,你得看看更新Globals.serviceUnavailable
的值是否需要同步。 (如果您可以部分成功删除该批用户 ID,则可能没有必要。)
此外,只有当您的线程池远小于提交的任务数时,这才有效,我在这里看到的就是这种情况。
我正在尝试实现示例应用程序来测试 Callable 和 ExecutorService 接口。
在我的应用程序中我有:
@Bean("fixedThreadPool")
public ExecutorService fixedThreadPool() {
return Executors.newFixedThreadPool(5);
}
然后:
public void removeUserIds(Set<String> userIds) {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://localhost:8080/remove");
final List<Callable<String>> callables = new ArrayList<>();
userIds.forEach(userId -> {
final Callable<String> task = () -> callServiceToRemove(builder,userId); //Call to remote service using RestTemplate
callables.add(task);
});
try {
final List<Future<String>> futureList =
executor.invokeAll(callables);
futureList.forEach(future -> {
try {
log.info(future.get());
} catch (final Exception e) {
log.error("Error : "+ e.getMessage());
}
});
} catch (final Exception e) {
log.error("Error Error Error : "+ e.getMessage());
} finally {
executor.shutdown();
}
}
当我使用 100 个 userIds 调用 removeUserIds() 方法时,它在快乐流程中工作正常,但如果服务不可用或关闭,第 100 次打印错误。如果服务不可用或关闭,我无法 stop/terminate 线程,因此服务不会发生进一步的调用。 任何人都可以在这里帮助解决这个问题,如果服务中断,我如何停止线程执行,或者在这里提出可行的解决方案?
将 callServiceToRemove 调用移到 try catch 块中。
在 catch 块中你可以使用 executor.shutdownNow();
shutdownNow() 尝试停止所有正在执行的任务,停止等待任务的处理,以及 returns 等待执行的任务列表。 此方法不会等待主动执行的任务终止并尝试强制停止它们。除了尽最大努力停止处理正在执行的任务之外,没有任何保证。此实现通过 Thread.interrupt() 取消任务,因此任何未能响应中断的任务可能永远不会终止。
与其说是编码问题,不如说这是设计问题。可能有几种方法。您可以以此为例:
使用全局标志
在实际触发远程服务调用之前寻找全局布尔标志,例如 Globals.serviceUnavailable
。 这个全局标志可以由遇到远程错误的第一个服务设置。这里是对代码的更改。
final Callable<String> task = () -> {
try{
if( !Globals.serviceUnavailable ) callServiceToRemove(builder,userId);
}
catch( ServiceUnavailableException e ){ //Or whatever your exception is
Globals.serviceUnavailable = true; //Notify the remaining tasks that the service is unavailable.
}
}
当然,你得看看更新Globals.serviceUnavailable
的值是否需要同步。 (如果您可以部分成功删除该批用户 ID,则可能没有必要。)
此外,只有当您的线程池远小于提交的任务数时,这才有效,我在这里看到的就是这种情况。