java超时控制
来源:互联网 发布:acg音乐知乎 编辑:程序博客网 时间:2024/05/29 14:32
超时控制是我们在系统中容易忽视却又不能忽视的功能。一方面,超时并不是我们系统的主要业务功能,大多数超时控制已经被虚拟机或者服务器实现了;另一方面,超时控制是许多业务必备的隐性功能,试想,一个web应用如果在10秒默认超时过后才响应,客户能够忍受吗?
这篇文章主要总结了一些常见的超时处理机制,以及适用的场景。
首先这里提出一个实际遇到的问题,大家可以思考如何解决。在调用corba连接服务器并获取数据时需要做超时处理,以便给用户一个良好的用户体验(如service.handshake(),握手的过程就是发起corba请求的过程),可惜的是,corba生成的代码并没有提供一个超时的参数入口(查过一下,似乎在corba中连接要设置超时必须配置他的policy,但是我对此一窍不通),在这种情况下,如何用我们已知的方法来完成这个任务呢。
其实这个问题基本思路跟线程有关系,因为超时意味着阻塞,如果只有主线程,则阻塞就意味着失去了控制,基本上就谈不上超时控制了(个人认为在线程运行中间自己判断并跳出循环的方式只是线程的操作方法,并不算超时控制)。如果有主从两个线程,则在从线程阻塞过程中知道从线程超时并将其终止,则达到我们的目的。最终的思想就是将其他超时问题转化为线程超时问题。
我们来看第一种方式,即网上常见的join方式。因为没有直接的线程方法提供超时参数,则可以转换下思维,利用已经提供超时的方法join。join是Thread类的方法,意为当前线程等待调用线程结束,最重要的是,join可以传入超时控制的参数,参照如下两个原型:
join(long millis) join(long millis, int nanos)那么是否可以在子线程中完成待超时任务,并join子线程,当join超时便回到主线程,然后在主线程中终止子线程。HttpClient中TimeoutController就是采用这个方式。这里task是子线程,可以完成类似corba连接之类的任务,在task join之后,当前运行线程便会等待task运行结束,并且只等待timeout时间,如果timeout过了子线程仍然没有结束,则主线程可以活过来并中断子线程。
public static void execute(Thread task, long timeout) throws TimeoutException { task.start(); try { task.join(timeout); } catch (InterruptedException e) { /* if somebody interrupts us he knowswhat he is doing */ } if (task.isAlive()) { task.interrupt(); throw new TimeoutException(); } }第一种方式没有那么直观,相对来说,join有些绕弯,有没有更直接点的方式?那就是用concurrent包的Future,future在得到计算结果时有个超时参数,利用此方式,可以有效的实现超时
ExecutorService executor = Executors.newSingleThreadExecutor(); FutureTask<LicenseService> future = new FutureTask<LicenseService>( new Callable<LicenseService>() { public LicenseService call() { LicenseServerAdapter adapter = new LicenseServerAdapter( info); LicenseService service = adapter.getLicenseService(); service.handShake(); return service; } }); executor.execute(future); return future.get(timeout * 1000, TimeUnit.MILLISECONDS);
说到超时,还有另外一个超时的场景,即web服务器的session超时。可以想象一下,如果自己实现了web服务器,需要实现session的功能,这时该如何实现session的超时呢?这个问题可以有很多方式解决,这里提供两个,第一是在每个session对象中存入一个时间戳,每次访问时先检查是否已经过期,如过期,则回收该session,如没有过期,则更新该时间戳,但是有个缺点,如果在过期后都不再次访问,则session永远不会被回收,当然,这里还是可以改进的,比如每次访问时检查所有session是否过期,又会带来效率的问题,有兴趣的朋友可以思考一下。另外一个方式是tomcat的实现,即一个后台线程定时对session做扫描,在扫描过程中实施session的时间戳更新或回收问题,参看代码
public void backgroundProcess() { count = (count + 1) % processExpiresFrequency; if (count == 0) processExpires(); } /** * Invalidate all sessions that have expired. */ public void processExpires() { long timeNow = System.currentTimeMillis(); Session sessions[] = findSessions(); int expireHere = 0 ; if(log.isDebugEnabled()) log.debug("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length); for (int i = 0; i < sessions.length; i++) { if (sessions[i]!=null && !sessions[i].isValid()) { expireHere++; } } long timeEnd = System.currentTimeMillis(); if(log.isDebugEnabled()) log.debug("End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) + " expired sessions: " + expireHere); processingTime += ( timeEnd - timeNow ); }
最后一个关于超时的话题也很有意思,在linux中c函数的api的connect方法是没有提供超时参数的,如下:
int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);那么java是如何通过native方法的包装提供出带参数的connect api呢?其实这里思路与之前是一致的,即寻找一个替代的有超时参数的方法。首先是设置connect为非阻塞,然后调用connect并立即返回,最后将写事件注册到带超时的poll。
/* * To do a timed connect we make the socket non-blocking * and poll with a timeout; */ if (attachTimeout > 0) { dbgsysConfigureBlocking(socketFD, JNI_FALSE); } err = dbgsysConnect(socketFD, (struct sockaddr *)&sa, sizeof(sa)); if (err == DBG_EINPROGRESS && attachTimeout > 0) { err = dbgsysFinishConnect(socketFD, (long)attachTimeout); if (err == DBG_ETIMEOUT) { dbgsysConfigureBlocking(socketFD, JNI_TRUE); RETURN_ERROR(JDWPTRANSPORT_ERROR_TIMEOUT, "connect timed out"); } }int dbgsysFinishConnect(int fd, long timeout) { int rv = dbgsysPoll(fd, 0, 1, timeout); if (rv == 0) { return DBG_ETIMEOUT; } if (rv > 0) { return 0; } return rv;}int dbgsysPoll(int fd, jboolean rd, jboolean wr, long timeout) { struct pollfd fds[1]; int rv; fds[0].fd = fd; fds[0].events = 0; if (rd) { fds[0].events |= POLLIN; } if (wr) { fds[0].events |= POLLOUT; } fds[0].revents = 0; rv = poll(&fds[0], 1, timeout); if (rv >= 0) { rv = 0; if (fds[0].revents & POLLIN) { rv |= DBG_POLLIN; } if (fds[0].revents & POLLOUT) { rv |= DBG_POLLOUT; } } return rv;}
参考文章:
http://blog.donews.com/maverick/archive/2005/08/10/502286.aspx
http://my.oschina.net/astute/blog/92339
- java超时控制
- Java 超时线程控制实现
- java线程 超时控制的实现
- Java执行Shell脚本超时控制
- Java线程超时控制的实现
- Java线程超时控制的实现
- java 1.7控制代码超时时间
- Java实现控制线程超时的简单方法
- Java 通过主线程实现子线程的超时控制
- java-实现资源访问的超时和并发控制
- Java 多线程设置线程超时时间之 Callable接口和Future接口 线程超时控制
- Java 多线程设置线程超时时间之 Callable接口和Future接口 超时控制
- TUXEDO超时控制全功略
- tuxedo 超时控制
- TUXEDO超时控制全功略
- shell脚本超时控制
- shell脚本超时控制
- ajax 超时控制
- TCP程序设计(单线程服务器和多线程服务器学习)
- Reading package lists... Error! 解决方案
- AJAX简单入门
- 谷歌,火狐链接无法点击
- windows api 大全
- java超时控制
- 分布式处理框架 hadoop 和 storm
- 探索--SQL存储过程(一)
- mysql连接查询 左,右,内连接
- sql find out record one-to-one
- 倒排索引
- 2013年新年目标
- elf文件动态加载
- Hibernate一级缓存,二级缓存