B/S结构的项目中有报表生成的处理,由于处理时间过长,采用多线程的方式做成。点击“报表生成”按钮提交多线程请求,服务端启动一个线程就立刻Response,提示客户到报表一览画面察看结果。原来对于客户端启用多线程并没有加以限制,实际运用中,客户可以不断的点击按钮提交线程请求,服务端就不断的生成子线程,有可能导致严重的后果。因此,考虑对客户端的请求加以限制,如果超过该机能的线程限制就必须等待,直到当前子线程运行完毕之后再请求新的子线程。
类中添加成员变量:
private static HashMap oThreadCount = new HashMap();
private final static int THREAD_LIMIT = 3;
在构造函数里,以当前的类的类名作为Map的Key值,保证多个报表类使用同一个“池”
public KjnBaseReport(String strReportID, String strTempFileName) {
String strClassName = this.getClass().getName();
// 一个客户端一个请求,每次请求时都会 New 一个实例, 必须保证同一个类的实例使用同一个计数器
if(!oThreadCount.containsKey(strClassName))
oThreadCount.put(strClassName, String.valueOf(0));
...
}
追加两个同步方法,一个增加,一个减少。保证对计数器的操作是同步的,是线程安全的。
private synchronized boolean IncreaseThreadCount() {
String strClassName = this.getClass().getName();
int nThreadCount = 0;
if(oThreadCount.containsKey(strClassName)) {
nThreadCount = Integer.parseInt(oThreadCount.get(strClassName).toString());
if(nThreadCount < THREAD_LIMIT) {
nThreadCount++;
oThreadCount.put(strClassName, String.valueOf(nThreadCount));
return true;
}
}
return false;
}
private synchronized boolean DecreaseThreadCount() {
String strClassName = this.getClass().getName();
int nThreadCount = 0;
if(oThreadCount.containsKey(strClassName)) {
nThreadCount = Integer.parseInt(oThreadCount.get(strClassName).toString());
if(nThreadCount > 0) {
nThreadCount--;
oThreadCount.put(strClassName, String.valueOf(nThreadCount));
return true;
}
}
return false;
}
修改异步调用“生成报表”的接口:
public synchronized void asyncExport(IStvDataSet oDataSet) {
if(this.oThread == null) {
this.oDataSet = oDataSet;
// 判断增加线程数目是否超过限制?
if(this.IncreaseThreadCount()) {
this.oThread = new Thread(this);
this.oThread.start();
}
// 如果超过限制,则抛出受限的异常(为了减少代码的修改量不改变方法的签名,只好抛出异常)
else
this.oException = new Exception("Thread Limit");
}
}
这样在Action中,如果该方法抛出异常(可能还要对Exception类型进行细分)就在jsp上注册另一段javascript提示用户当前系统正忙,请稍后再请求。