我们在开发的时候,常常会有这样的需要,需要保证某个操作只运行一段时间,如果超时了,就执行对应的超时操作。
比如,在读取网络请求的时候,我们希望3秒内能读到数据,如果超过了3秒没有读到,那么就不读了,提示用户,超时了,需要重试。
比如,我们开启了一个进程来执行一条命令,这个命令可能是批量处理一批文件并生成一个报告,或者其它,我们知道这个命令肯定不会执行超过30分钟,那么,我们需要给它设定一个时间,如果超时了,那么我们就杀掉该进程, 并清除掉错误的生成数据。
比如,我们通过USB接口将设备如电纸书,MP4等连接到电脑,如果尝试连接了一段时间而没有连接上,那么会给出指导,让用户检查设备或者进行重试。
诸如此类,还有很多。
对于第一种情况,我们还有选择,例如,我们可以通过设置socket的timeout时间来达到这个目的,但是对于大多数情况,它们都没有给我们太多这样原生的方式进行选择。
但是,对于这样的需求,apache-common-exec包中的WatchDog还是给了我们一个很好的例子。
这是一个简单的监听者模式的实现,WatchDog是Subject,TimeoutObserver自然就是Observer,从上图可以看出,WatchDog有着经典Subject的3个职责:
1. 管理Observer的职责,如addTimeoutObserver(),removeTimeoutObserver()方法
2. 触发Observer的职责,如fireTimeoutOccured()
3. 自己本身的业务逻辑,在run()方法中。
从类图上看,可以看出WatchDog类实现了Runnable接口,它在运行时实际上是一个deamon线程,看它的start()方法的实现:
public synchronized void start() { stopped = false; Thread t = new Thread(this, "WATCHDOG"); t.setDaemon(true); t.start(); }
而它自己本身的业务逻辑则很简单,就是判断什么时候超时然后触发TimeoutObserver的timeoutOccured(w : Watchdog) : void 方法:
public synchronized void run() { final long until = System.currentTimeMillis() + timeout; long now; while (!stopped && until > (now = System.currentTimeMillis())) { try { wait(until - now); } catch (InterruptedException e) { } } if (!stopped) { fireTimeoutOccured(); //trigger TimeoutObserver#timeoutOccred() operation } } protected final void fireTimeoutOccured() { Enumeration e = observers.elements(); while (e.hasMoreElements()) { ((TimeoutObserver) e.nextElement()).timeoutOccured(this); } }
代码很简单,是不是?当我们有超时了需要进行某种操作的需求时,只需要将你需要进行的操作放到实现了TimeoutObserver接口的类中,比如:
public class NotifyUserTimeout implements TimeoutObserver { @Override public void timeoutOccured(Watchdog w) { System.out.println("Timeout happens.. exit the applicaton now"); System.exit(-1); } }
然后调用WatchDog类进行超时时间的设置以及注册实现的TimeoutObserver即可。
static main(args) { Watchdog watchDog=new Watchdog(3000);//set timeout for 3 seconds watchDog.addTimeoutObserver new NotifyUserTimeout() watchDog.start(); //simulate time-consuming task.. Thread.sleep 5000 println "finish normally.." }
从上可以看到,我们模拟了正常的操作大概需要5秒,但是设置了超时的时限为3秒,当正常的操作超时后,会进行提示用户并且退出,上面的例子的结果输出为:
Timeout happens.. exit the applicaton now
看吧,两个简单的类,一个经典的设计模式,便很好的诠释了超时了该怎么办这个需求。如果你有这样的需求,不妨去试一试。