背景描述
调用关联方接口,控制自身调用的时间;理论上由接口发布方来控制调用超时时间更好,例如feign调用可以直接通过注解配置,单个接口可以设置Request控制,这里记录下调用方的控制方式
常用方式:FutureTask自带方法
/**
* @throws CancellationException {@inheritDoc}
*/
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
return report(s);
}
与不带参数的get一样,使用时会阻塞主线程。如果对应场景有需要可以单独再启一个线程等待结果返回。
这里直接贴测试的主要代码:
public class MainThread {
public static void main(String[] args) {
//amend commit test03
System.currentTimeMillis();
ThreadPoolTaskExecutor executorThreadPool = new ThreadPoolTaskExecutor();
ThreadPoolTaskExecutor monitorThreadPool = new ThreadPoolTaskExecutor();
List<ThreadMonitorTask> taskList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
int finalI = i;
Apple red = new Apple("red", 200 + finalI);
FutureTask<Apple> appleFutureTask = new FutureTask<>(() -> {
//do something
return red;
});
taskList.add(new ThreadMonitorTask<Apple>(red, appleFutureTask));
}
ThreadTaskExecutor executor = new ThreadTaskExecutor(taskList, executorThreadPool, monitorThreadPool);
executor.execute();
List<ThreadMonitorTask> retList = executor.getList();
}
}
分析一下主要的代码:
单独启线程来获取future task的结果,近似理解为开个线程监控业务逻辑的返回值。也可以采用缓存(业务线程处理完写结果到缓存中,主线程定时去取),或者主线程不在乎等待时间,可以在主线的流程中get
public void run() {
try {
//默认等待时长10S
result = futureTask.get(10, TimeUnit.SECONDS);
sts = Boolean.TRUE;
} catch (Exception e) {
//对超时异常和主动打断异常做特殊处理
if(e instanceof TimeoutException || e instanceof CancellationException){
//do something
errorMsg = "timeout";
}
} finally {
sts = Boolean.FALSE;
System.out.println("打印入参和结果");
}
}
对整体调用的时间也有控制的话,需要下面的逻辑来控制:
1.设置任务提交耗时
2.考虑超时处理
3.isDone判断及业务线程是否处理完成判断
public void execute(){
logger.info("ThreadTaskExecutor execute start");
long startTime = System.currentTimeMillis();
//提交任务
submitTask();
while (true) {
//监控时间
long currentTimeMillis = System.currentTimeMillis();
if (currentTimeMillis - startTime > timeout) {
logger.info("total timeout");
//总体超时
stop();
return;
}
boolean isRun = Boolean.FALSE;
for (ThreadMonitorTask threadMonitorTask : list) {
if (!threadMonitorTask.isDone()) {
//任务执行中
isRun = Boolean.TRUE;
}
}
if (!isRun) {
//任务执行完成
return;
}
}
}
最终 List retList = executor.getList();
返回的结果里包含执行标识,返回结果,异常信息等
自定义的处理类,完整代码如下
public class ThreadMonitorTask<T> implements Runnable {
/**
* 线程返回结果,根据自己定义的类型进行转换
*/
private T result;
/**
* 可定义成范型,作为参数,或者要和结果进行逻辑交互的对象
*/
private Apple apple;
/**
* 传入的需要进行监控的task
*/
private FutureTask<T> futureTask;
/**
* 执行状态,增加标识判断对应的结果是否成功获取
*/
private Boolean sts;
/**
* 预留保存异常信息
*/
private String errorMsg;
public ThreadMonitorTask(Apple apple, FutureTask<T> futureTask) {
this.apple = apple;
this.futureTask = futureTask;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
public Apple getApple() {
return apple;
}
public void setApple(Apple apple) {
this.apple = apple;
}
public Boolean getSts() {
return sts;
}
public void setSts(Boolean sts) {
this.sts = sts;
}
public FutureTask<T> getFutureTask() {
return futureTask;
}
public void setFutureTask(FutureTask<T> futureTask) {
this.futureTask = futureTask;
}
@Override
public void run() {
try {
//默认等待时长
result = futureTask.get(10, TimeUnit.SECONDS);
sts = Boolean.TRUE;
} catch (Exception e) {
//对超时异常和主动打断异常做特殊处理
if(e instanceof TimeoutException || e instanceof CancellationException){
//do something
errorMsg = "timeout";
}
} finally {
sts = Boolean.FALSE;
System.out.println("打印入参和结果");
}
}
/**
* 手动打断
*/
public void cancel(){
futureTask.cancel(true);
}
/**
* 是否执行完
* @return
*/
public boolean isDone(){
return futureTask.isDone();
}
}
执行类:
public class ThreadTaskExecutor {
private static final Logger logger = LoggerFactory.getLogger(ThreadTaskExecutor.class);
/**
*监控任务
*/
private List<ThreadMonitorTask> list;
/**
* 执行起限制总时间,单位默认:ms
*/
private long timeout = 30000;
private ThreadPoolTaskExecutor taskExecutor;
private ThreadPoolTaskExecutor monitorExecutor;
public ThreadTaskExecutor(List<ThreadMonitorTask> list, ThreadPoolTaskExecutor taskExecutor,
ThreadPoolTaskExecutor monitorExecutor) {
this.list = list;
this.taskExecutor = taskExecutor;
this.monitorExecutor = monitorExecutor;
}
public List<ThreadMonitorTask> getList() {
return list;
}
public void setList(List<ThreadMonitorTask> list) {
this.list = list;
}
public void execute(){
logger.info("ThreadTaskExecutor execute start");
long startTime = System.currentTimeMillis();
//提交任务
submitTask();
while (true) {
//监控时间
long currentTimeMillis = System.currentTimeMillis();
if (currentTimeMillis - startTime > timeout) {
logger.info("total timeout");
//总体超时
stop();
return;
}
boolean isRun = Boolean.FALSE;
for (ThreadMonitorTask threadMonitorTask : list) {
if (!threadMonitorTask.isDone()) {
//任务执行中
isRun = Boolean.TRUE;
}
}
if (!isRun) {
//任务执行完成
return;
}
}
}
private void submitTask(){
//启动任务
for (ThreadMonitorTask threadMonitorTask : list) {
taskExecutor.submit(threadMonitorTask.getFutureTask());
}
//启动监控
for (ThreadMonitorTask threadMonitorTask : list) {
monitorExecutor.submit(threadMonitorTask);
}
}
private void stop(){
for (ThreadMonitorTask threadMonitorTask : list) {
threadMonitorTask.cancel();
}
}
}
问题记录:FutureTask.isDone returns true when task has not yet completed
描述:
执行器通过FutureTask.isDone方法判断线程是否执行完成,但是事实上,jdk1.8比较不同版本是存在使用风险的
先看FutureTask中的get方法,等待的条件都是<=COMPLETING,状态种类如下:
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
但是,isDone方法如下:
也就是说,当状态为COMPLETING = 1的时候,是存在线程执行对应的结果尚未返回即现场尚未结束,同时isDone返回true,所以如果以此状态值判断是否执行完成是存在问题的
问题参考:https://bugs.openjdk.org/browse/JDK-8073704