一、背景
在做产品时发现在某一逻辑中需要加入超时的限制,但原有程序块并不支持也不能修改。所以我需要在外部包装一层,加入超时逻辑。
二、分析(修正2)
想控制一段代码段的执行,我们可以采用开启新线程(Thread)的方式来控制。判断线程超时有多种方式,为了降低编码量我采用 CountDownLatch.await(...) 方法,将异步的执行线程转换为同步方式。
逻辑过程:
a、将需要限制的程序段放入新创建的内嵌线程(Thread)执行 b、Thread 构造时,设置 new CountDownLatch(1); c、启动内嵌线程 Thread.start() d、CountDownLatch.await 等待内嵌线程中 latch 被 countDown() e1、内嵌线程逻辑在 await 超时时间内完成,调用 countDown 释放 await f、await 返回 true,说明执行没有超时 e2、内嵌线程逻辑没有在 await 超时时间内完成(超时),await 会返回 false g、await 返回 false,说明执行超时
三、限时程序辅助类(修正2)
TimeoutExecution 超时限制执行辅助类。为了获取内嵌执行代码段的返回值与异常,加入了 result 与 exception 成员变量。
package org.noahx.stimeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* 超时限制执行辅助类(线程不安全,使用时请创建新实例)
*
* Created by noah on 6/10/14.
*/
public abstract class TimeoutExecution {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private volatile Exception exception;
private CountDownLatch latch;
private T result;
/**
* 需要实现的具体执行内容
*
* @return 返回值
* @throws Exception 异常
*/
protected abstract T execute() throws Exception;
/**
* 超时限制执行
*
* @param timeout 超时时间数值
* @param unit 超时时间单位
* @return true,没有超时正常返回,false 超时
*/
public boolean execute(int timeout, TimeUnit unit) {
result = null; //清楚上次数据
latch = new CountDownLatch(1);
InnerThread innerThread = new InnerThread(); //初始化内部执行线程
innerThread.start();
boolean isNotTimeout = true;
try {
isNotTimeout = latch.await(timeout, unit); //等待执行完毕
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
if (!isNotTimeout) {
innerThread.interrupt(); //终止连接线程
}
return isNotTimeout;
}
private class InnerThread extends Thread {
@Override
public void run() {
try {
result = execute(); //存储执行结果
} catch (Exception e) {
exception = e; //存储执行异常
} finally {
latch.countDown(); //释放等待
}
}
}
public T getResult() {
return result;
}
public Exception getException() {
return exception;
}
}
[修正1]:TimeoutExecution2 超时限制执行辅助类(使用 FutureTask,效果一样)。
package org.noahx.stimeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.*;
/**
* 超时限制执行辅助类(线程不安全,使用时请创建新实例)
*
* Created by noah on 6/10/14.
*/
public abstract class TimeoutExecution2 implements Callable {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private volatile Exception exception;
private T result;
/**
* 需要实现的具体执行内容
*
* @return 返回值
* @throws Exception 异常
*/
protected abstract T execute() throws Exception;
/**
* 超时限制执行
*
* @param timeout 超时时间数值
* @param unit 超时时间单位
* @return true,没有超时正常返回,false 超时
*/
public boolean execute(int timeout, TimeUnit unit) {
result = null; //清楚上次数据
FutureTask futureTask = new FutureTask(this);
boolean isNotTimeout = true;
try {
new Thread(futureTask).start();
result = futureTask.get(timeout, unit);
} catch (InterruptedException e) {
exception = e;
} catch (ExecutionException e) {
exception = (Exception) e.getCause();
} catch (TimeoutException e) {
isNotTimeout = false;
} finally{ // ------[修正2]+
futureTask.cancel(true); // ------[修正2]+
}
return isNotTimeout;
}
@Override
public T call() throws Exception {
return execute();
}
public T getResult() {
return result;
}
public Exception getException() {
return exception;
}
}
四、使用辅助类(样例)
1、TimeoutExample 样例类
具体详见注释,模拟了4种执行情况。
package org.noahx.stimeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.impl.SimpleLogger;
import java.util.concurrent.TimeUnit;
/**
* Created by noah on 6/10/14.
*/
public class TimeoutExample {
static {
System.setProperty(SimpleLogger.SHOW_DATE_TIME_KEY,"true");
}
private static Logger logger = LoggerFactory.getLogger(TimeoutExample.class);
public static void main(String[] args) {
logger.info("1:---------------[正常执行不超时]");
TimeoutExecution timeoutExecution1 = new TimeoutExecution() { //设置5s 超时
@Override
protected Void execute() throws Exception {
logger.info("exe:1");
return null;
}
};
boolean execute1 = timeoutExecution1.execute(5, TimeUnit.SECONDS);
printResult(execute1, timeoutExecution1); //设置5s 超时
logger.info("2:---------------[执行超时]");
TimeoutExecution timeoutExecution2 = new TimeoutExecution() {
@Override
protected Void execute() throws Exception {
logger.info("exe:2");
Thread.sleep(10000); //模拟执行消耗 10s
return null;
}
};
boolean execute2 = timeoutExecution2.execute(5, TimeUnit.SECONDS);
printResult(execute2, timeoutExecution2); //设置5s 超时
logger.info("3:---------------[有返回值]");
TimeoutExecution timeoutExecution3 = new TimeoutExecution() {
@Override
protected Integer execute() throws Exception {
logger.info("exe:3");
Thread.sleep(1000); //模拟执行消耗 1s
return 123;
}
};
boolean execute3 = timeoutExecution3.execute(5, TimeUnit.SECONDS);
printResult(execute3, timeoutExecution3);
logger.info("4:---------------[没有超时,但执行有异常]");
TimeoutExecution timeoutExecution4 = new TimeoutExecution() {
@Override
protected Integer execute() throws Exception {
logger.info("exe:4");
Thread.sleep(1000); //模拟执行消耗 1s
if (true) {
throw new RuntimeException("模拟异常");
}
return 123;
}
};
boolean execute4 = timeoutExecution4.execute(5, TimeUnit.SECONDS);
printResult(execute4, timeoutExecution4);
}
private static void printResult(boolean isNotTimeout, TimeoutExecution execution) {
if (isNotTimeout) {
logger.info("Ok!!");
logger.info("result=" + execution.getResult());
logger.info("e=" + execution.getException());
} else {
logger.info("Timeout!!");
logger.info("result=" + execution.getResult());
logger.info("e=" + execution.getException());
}
}
}
2、执行输出结果
9 [main] INFO org.noahx.stimeout.TimeoutExample - 1:---------------[正常执行不超时]
16 [Thread-0] INFO org.noahx.stimeout.TimeoutExample - exe:1
17 [main] INFO org.noahx.stimeout.TimeoutExample - Ok!!
17 [main] INFO org.noahx.stimeout.TimeoutExample - result=null
17 [main] INFO org.noahx.stimeout.TimeoutExample - e=null
17 [main] INFO org.noahx.stimeout.TimeoutExample - 2:---------------[执行超时]
20 [Thread-1] INFO org.noahx.stimeout.TimeoutExample - exe:2
5020 [main] INFO org.noahx.stimeout.TimeoutExample - Timeout!!
5020 [main] INFO org.noahx.stimeout.TimeoutExample - result=null
5020 [main] INFO org.noahx.stimeout.TimeoutExample - e=null
5020 [main] INFO org.noahx.stimeout.TimeoutExample - 3:---------------[有返回值]
5025 [Thread-2] INFO org.noahx.stimeout.TimeoutExample - exe:3
6026 [main] INFO org.noahx.stimeout.TimeoutExample - Ok!!
6026 [main] INFO org.noahx.stimeout.TimeoutExample - result=123
6026 [main] INFO org.noahx.stimeout.TimeoutExample - e=null
6026 [main] INFO org.noahx.stimeout.TimeoutExample - 4:---------------[没有超时,但执行有异常]
6032 [Thread-3] INFO org.noahx.stimeout.TimeoutExample - exe:4
7032 [main] INFO org.noahx.stimeout.TimeoutExample - Ok!!
7032 [main] INFO org.noahx.stimeout.TimeoutExample - result=null
7032 [main] INFO org.noahx.stimeout.TimeoutExample - e=java.lang.RuntimeException: 模拟异常
五、源码下载
http://1drv.ms/1zHq3Oe