用M4构建一个了一个项目,在使用发现DefaultIoFuture的一个小问题。贴出来供大家参考一下。
先简述下IoFuture接口的作用,这个接口目的在于等待异步执行的任务,并在任务完成时通知一个监听着.而DefaultIoFuture是这个接口的实现类。在Mina中有许多Future都从这个类中派生,完成定制的任务。接口方法通俗的很,看看他们长什么样:
public interface IoFuture {
IoSession getSession();
IoFuture await() throws InterruptedException;
boolean await(long timeout, TimeUnit unit) throws InterruptedException;
boolean await(long timeoutMillis) throws InterruptedException;
IoFuture awaitUninterruptibly();
boolean awaitUninterruptibly(long timeout, TimeUnit unit);
boolean awaitUninterruptibly(long timeoutMillis);
boolean isDone();
IoFuture addListener(IoFutureListener<?> listener);
IoFuture removeListener(IoFutureListener<?> listener);
//过期的方法
void join();
boolean join(long timeoutMillis);
}
可以看见接口很简洁,关于等待的接口有两对,一对是受异常检查的,另外一对则是不受异常检查。那么在DefaultIoFuture中BUG体现在awaitUninterruptibly()这个方法中,按常理这个方法应该一直等待,直到setValue方法设置值后将等待的线程唤醒并执行IoFutureListener。那么看看关于awaitUninterruptibly()方法的实现:
public IoFuture awaitUninterruptibly() {
try {
//false的意思调用await0不抛出异常,因为await0还可以让await(long timeoutMillis)等方法复用
await0(Long.MAX_VALUE, false);
} catch ( InterruptedException ie) {
// Do nothing : this catch is just mandatory by contract
}
return this;
}
private boolean await0(long timeoutMillis, boolean interruptable) throws InterruptedException {
//这里就是BUG出现的地方了,这时候endTime会变成负数
long endTime = System.currentTimeMillis() + timeoutMillis;
synchronized (lock) {
if (ready) {
return ready;
} else if (timeoutMillis <= 0) {
return ready;
}
waiters++;
try {
for (;;) {
try {
long timeOut = Math.min(timeoutMillis, DEAD_LOCK_CHECK_INTERVAL);
lock.wait(timeOut);
} catch (InterruptedException e) {
if (interruptable) {
throw e;
}
}
if (ready) {
return true;
} else {
//而这里将是-9223370799985705809 < System.currentTimeMillis() 这个条件永远成立.
//所以导致5秒钟后退出
if (endTime < System.currentTimeMillis()) {
return ready;
}
}
}
} finally {
waiters--;
if (!ready) {
checkDeadLock();
}
}
}
}
很明显的一个失误, 我想之所以没有发现是因为他们没有经过压力测试,又或者压力测试没有超时上5秒以上。但是总感觉不应该啊...又或者是我弄错了?
修正只需要简单的在上面第15行换成这个:
long endTime = Math.max(System.currentTimeMillis() + timeoutMillis,timeoutMillis);