我们在开发过程中可能会遇到这样的场景:在执行一个耗时操作的时候,如果在规定的时间内处理完成了,则返回正确的结果,否则视为超时任务,这个时候我们将不再等待(不再执行)该耗时操作,直接告诉调用者:这个任务由于耗时过多,被取消了。
java早已经给我们提供了解决方案。jdk1.5自带的并发库中Future类就能满足这个需求。Future类中重要方法包括get()和cancel()。get()获取数据对象,如果数据没有加载,就会阻塞直到取到数据,而 cancel()是取消数据加载。另外一个get(timeout)操作,表示如果在timeout时间内没有取到就失败返回,而不再阻塞。
利用泛型和函数式接口编写一个工具类,可以让超时处理更方便,而不用到处写代码。
/**
* TimeoutUtil <br>
*
* @author lys
* @date 2021/2/25
*/
@Slf4j
@Component
@NoArgsConstructor
public class TimeoutUtil {
private ExecutorService executorService;
public TimeoutUtil(ExecutorService executorService) {
this.executorService = executorService;
}
/**
* 有超时限制的方法
*
* @param bizSupplier 业务函数
* @param timeout 超时时间,ms
* @return 返回值
*/
public <R> Result<R> doWithTimeLimit(Supplier<R> bizSupplier, int timeout) {
return doWithTimeLimit(bizSupplier, null, timeout);
}
/**
* 有超时限制的方法
*
* @param bizSupplier 业务函数
* @param defaultResult 默认值
* @param timeout 超时时间,ms
* @return 返回值
*/
public <R> Result<R> doWithTimeLimit(Supplier<R> bizSupplier, R defaultResult, int timeout) {
R result;
String errMsg = "Null value";
FutureTask<R> futureTask = new FutureTask<>(bizSupplier::get);
executorService.execute(futureTask);
try {
result = futureTask.get(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
errMsg = String.format("doWithTimeLimit执行超过%d毫秒,强制结束", timeout);
log.error(errMsg, e);
futureTask.cancel(true);
result = defaultResult;
}
return of(result, errMsg);
}
/**
* 随机耗时的测试方法
*/
private String randomSpentTime() {
Random random = new Random();
int time = (random.nextInt(10) + 1) * 1000;
log.info("预计randomSpentTime方法执行将耗时: " + time + "毫秒");
try {
Thread.sleep(time);
} catch (Exception e) {
}
return "randomSpentTime --> " + time;
}
public static void main(String[] args) throws Exception {
ExecutorService executorService = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
runnable -> {
Thread thread = new Thread(runnable);
// 以守护线程方式启动
thread.setDaemon(true);
return thread;
});
TimeoutUtil timeoutUtil = new TimeoutUtil(executorService);
for (int i = 1; i <= 10; i++) {
log.info("\n=============第{}次超时测试=============", i);
Thread.sleep(6000);
long start = System.currentTimeMillis();
String result = timeoutUtil.doWithTimeLimit(() -> timeoutUtil.randomSpentTime(), 5000).getOrElse("默认");
log.info("doWithTimeLimit方法实际耗时{}毫秒,结果:{}", System.currentTimeMillis() - start, result);
}
}
}
测结果如下:
Result是一套函数式的返回值包装类,参考《Java函数式编程》
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.CollectionUtils;
import java.io.Serializable;
import java.util.Collection;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Result接口表示函数调用的结果
*
* @author lys
* @date 2020/06/08
* @since 1.0.0
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public abstract class Result<V> implements Serializable {
/**
* serialVersionUID
*/
private static final long serialVersionUID = -8917987013371280396L;
/**
* 单例的empty对象,表示空
*/
private static final Result EMPTY = new Empty<>();
/**
* 单例的failure对象,表示null
*/
private static final Result FAILURE_NULL = failure("Null value");
//==================================静态方法Start==================================
/**
* 返回成功子类对象
*
* @param value 返回值
* @param <V> 返回值类型
* @return
*/
public static <V> Result<V> success(V value) {
return new Success<>(value);
}
/**
* 返回为空子类对象
*
* @param <V> 返回值类型
* @return
*/
public static <V> Result<V> empty() {
return EMPTY;
}
public static <V, U> Result<V> failure(Failure<U> failure) {
return new Failure<>(failure.exception);
}
/**
* 返回失败子类对象
*
* @param errMsg 错误信息
* @param <V> 返回值类型
* @return
*/
public static <V> Result<V> failure(String errMsg) {
return new Failure<>(errMsg);
}
/**
* 返回失败子类对象
*
* @param ex 失败异常
* @param <V> 返回值类型
* @return
*/
public static <V> Result<V> failure(Exception ex) {
return new Failure<>(ex);
}
/**
* 返回失败子类对象
*
* @param ex 失败运行时异常
* @param <V> 返回值类型
* @return
*/
public static <V> Result<V> failure(RuntimeException ex) {
return new Failure<>(ex);
}
/**
* of工厂方法
*
* @param value
* @param <V>
* @return Result<V>
*/
public static <V> Result<V> of(V value) {
return value != null ?
success(value) : FAILURE_NULL;
}
/**
* of工厂方法
*
* @param value
* @param errMsg 错误信息
* @param <V>
* @return Result<V>
*/
public static <V> Result<V> of(V value, String errMsg) {
return value != null ?
success(value) : failure(errMsg);
}
/**
* 获得调用结果或默认值
*
* @param defaultValue 默认值
* @return
*/
public abstract V getOrElse(final V defaultValue);
//==================================静态方法End==================================
//==================================抽象方法Start==================================
/**
* 获得调用结果或默认值
*
* @param defaultValue 默认值Supplier
* @return
*/
public abstract V getOrElse(final Supplier<V> defaultValue);
/**
* map方法通过应用从V到U的函数将Result<V>更改为Result<U>
*
* @param f 从V到U的函数
* @return Result<U>
*/
public abstract <U> Result<U> map(final Function<V, U> f);
/**
* flatMap方法通过应用从V到Result<U>的函数将Result<V>更改为Result<U>
*
* @param f 从V到Result<U>的函数
* @return Result<U>
*/
public abstract <U> Result<U> flatMap(final Function<V, Result<U>> f);
/**
* 失败映射方法<br>
* 以String为参数,并以其为错误消息将一个Failure转换为另一个Failure,如果Result为Empty或Success只返回this
* @param s
* @return
*/
public abstract Result<V> mapFailure(String s);
/**
* 针对Empty的映射
*
* @return 当前对象为Empty时入返回success对象,否则返回failure
*/
public abstract Result<Nothing> mapEmpty();
/**
* 以String为参数,并以其为错误消息将一个Empty转换为一个Failure;
* 如果Result为Failure则以输入字符串为错误消息转换为另一个Failure;
* 如果Result为Success只返回this。<br>
* @param message 错误消息
* @return
*/
public abstract Result<V> failIfEmpty(String message);
/**
* forEach方法,以一个Effect为参数,并将其应用于包装值
*
* @param ef 函数作用,用于对包装值执行具体的业务逻辑
* @return
*/
public abstract void forEach(Effect<V> ef);
/**
* 判断是否为成功结果
* @return 是否为成功结果标识
*/
public abstract Boolean isSuccess();
/**
* 判断是否为失败结果
* @return 是否为失败结果标识
*/
public abstract Boolean isFailure();
/**
* 判断是否为空结果
* @return 是否为空结果标识
*/
public abstract Boolean isEmpty();
//==================================抽象方法End==================================
//==================================非抽象方法Start==================================
/**
* 从Result<V>中检索值,为空返回默认值
*
* @return
*/
public Result<V> orElse(Supplier<Result<V>> defaultValue) {
return map(x -> this).getOrElse(defaultValue);
}
//==================================非抽象方法End==================================
//==================================子类实现Start==================================
/**
* 表示为空结果
*/
private static class Empty<V> extends Result<V> {
private static final long serialVersionUID = 5115067706362088704L;
private Empty() {
super();
}
@Override
public V getOrElse(V defaultValue) {
return defaultValue;
}
@Override
public <U> Result<U> map(Function<V, U> f) {
return empty();
}
@Override
public <U> Result<U> flatMap(Function<V, Result<U>> f) {
return empty();
}
/**
* 以String为参数,并以其为错误消息将一个Failure转换为另一个Failure,如果Result为Empty或Success只返回this
*
* @param s 错误消息
* @return
*/
@Override
public Result<V> mapFailure(String s) {
return this;
}
@Override
public Result<V> mapFailure(Function<Exception, Exception> exConvertor) {
return this;
}
/**
* 针对Empty的映射
*
* @return 当前对象为Empty时入返回success对象,否则返回failure
*/
@Override
public Result<Nothing> mapEmpty() {
return success(Nothing.INSTANCE);
}
/**
* 以String为参数,并以其为错误消息将一个Empty转换为一个Failure;
* 如果Result为Failure则以输入字符串为错误消息转换为另一个Failure;
* 如果Result为Success只返回this。<br>
*
* @param message 错误消息
* @return
*/
@Override
public Result<V> failIfEmpty(String message) {
return failure(message);
}
/**
* forEach方法,以一个Effect为参数,并将其应用于包装值
*
* @param ef 函数作用,用于对包装值执行具体的业务逻辑
* @return
*/
@Override
public void forEach(Effect<V> ef) {
// Empty. Do nothing
}
/**
* 判断是否为成功结果
*
* @return 是否为成功结果标识
*/
@Override
public Boolean isSuccess() {
return false;
}
/**
* 判断是否为失败结果
*
* @return 是否为失败结果标识
*/
@Override
public Boolean isFailure() {
return false;
}
/**
* 判断是否为空结果
*
* @return 是否为空结果标识
*/
@Override
public Boolean isEmpty() {
return true;
}
@Override
public String toString() {
return "Empty()";
}
}
/**
* 表示成功结果
*/
private static class Success<V> extends Result<V> {
/**
* serialVersionUID
*/
private static final long serialVersionUID = 633612507243796152L;
private final V value;
private Success(V value) {
super();
this.value = value;
}
@Override
public String toString() {
return String.format("Success(%s)", value != null ? value.toString() : null);
}
/**
* 获得调用结果或默认值
*
* @param defaultValue 默认值
* @return
*/
@Override
public V getOrElse(V defaultValue) {
return value;
}
/**
* map方法通过应用从V到U的函数将Result<V>更改为Result<U>
*
* @param f 从V到U的函数
* @return Result<U>
*/
@Override
public <U> Result<U> map(Function<V, U> f) {
try {
return success(f.apply(value));
} catch (Exception e) {
return failure(e);
}
}
/**
* flatMap方法通过应用从V到Result<U>的函数将Result<V>更改为Result<U>
*
* @param f 从V到Result<U>的函数
* @return Result<U>
*/
@Override
public <U> Result<U> flatMap(Function<V, Result<U>> f) {
try {
return f.apply(value);
} catch (Exception e) {
return failure(e);
}
}
/**
* 以String为参数,并以其为错误消息将一个Failure转换为另一个Failure,如果Result为Empty或Success只返回this
*
* @param s 错误消息
* @return
*/
@Override
public Result<V> mapFailure(String s) {
return this;
}
/**
* 针对Empty的映射
*
* @return 当前对象为Empty时入返回success对象,否则返回failure
*/
@Override
public Result<Nothing> mapEmpty() {
return failure("Not empty");
}
/**
* 以String为参数,并以其为错误消息将一个Empty转换为一个Failure;
* 如果Result为Failure则以输入字符串为错误消息转换为另一个Failure;
* 如果Result为Success只返回this。<br>
*
* @param message 错误消息
* @return
*/
@Override
public Result<V> failIfEmpty(String message) {
return this;
}
/**
* forEach方法,以一个Effect为参数,并将其应用于包装值
*
* @param ef 函数作用,用于对包装值执行具体的业务逻辑
* @return
*/
@Override
public void forEach(Effect<V> ef) {
ef.apply(value);
}
/**
* 判断是否为成功结果
*
* @return 是否为成功结果标识
*/
@Override
public Boolean isSuccess() {
return true;
}
/**
* 判断是否为失败结果
*
* @return 是否为失败结果标识
*/
@Override
public Boolean isFailure() {
return false;
}
/**
* 判断是否为空结果
*
* @return 是否为空结果标识
*/
@Override
public Boolean isEmpty() {
return false;
}
}
/**
* 表示失败结果
*/
private static class Failure<V> extends Empty<V> {
/**
* serialVersionUID
*/
private static final long serialVersionUID = 4991495838920816791L;
private final RuntimeException exception;
/**
* 构造函数
*
* @param errMsg 错误信息
*/
private Failure(String errMsg) {
super();
this.exception = new IllegalStateException(errMsg);
}
private Failure(Exception exception) {
super();
this.exception = new IllegalStateException(exception.getMessage(), exception);
}
@Override
public String toString() {
return String.format("Failure(%s)", exception.getMessage());
}
/**
* map方法通过应用从V到U的函数将Result<V>更改为Result<U>
*
* @param f 从V到U的函数
* @return Result<U>
*/
@Override
public <U> Result<U> map(Function<V, U> f) {
return failure(exception);
}
/**
* flatMap方法通过应用从V到Result<U>的函数将Result<V>更改为Result<U>
*
* @param f 从V到Result<U>的函数
* @return Result<U>
*/
@Override
public <U> Result<U> flatMap(Function<V, Result<U>> f) {
return failure(exception);
}
/**
* 针对Empty的映射
*
* @return 当前对象为Empty时入返回success对象,否则返回failure
*/
@Override
public Result<Nothing> mapEmpty() {
return failure(this);
}
/**
* 判断是否为失败结果
*
* @return 是否为失败结果标识
*/
@Override
public Boolean isFailure() {
return true;
}
}
//==================================子类实现End==================================
}
参考:http://blog.youkuaiyun.com/a1015088819/article/details/53099943
https://blog.youkuaiyun.com/ithouse/article/details/78257418