在有些方法里, 比如登陆,一般需要顺序调用多个系统功能才能完成初始化操作.
比如 安全校验-》获取用户权限-》获取用户设置信息-》获取系统字典 等等, 在某些EAI模式的应用里,这些调用可能都是远程调用,每个调用时间可能从0.5秒到数秒不等, 这样累计下来用户一个登陆操作,在后台处理时间可能就有3,4秒,加上网络传输延时,会主观上给用户造成系统很慢的感觉。
在单个方法调用效率无法改进的前提下,一个简单的办法是将调用方式由串行改为并行,即以多个thread分别去完成方法调用,最后汇总。
在java 5以前,因为线程的特性限制,必须手工编码实现线程完成状态的调用检查。
比如
// thread
public void run() {
......
finished = true;
}
public boolean isFinished() {
return finished;
}
// 以阻塞循环的方式检查是否执行完毕
while(!remoteCall.isFinished()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
这样的代码是繁琐,较低效率,也容易出错的,尤其在线程多于2个的时候。而在服务器方代码中过多的线程使用又可能会导致内存溢出和资源泄露。
今天翻书的时候发现java 5中的cucurrent包对此进行了改进 继承自Callable 的类有能力将执行结果返回,并且可以自行检查执行是否结束。
public class RandomPrimeSearch implements Callable<BigInteger> {
public BigInteger call( ) {
// do operation here
return BigInteger.probablePrime(bitSize, prng);
}
}
FutureTask<BigInteger> task = new FutureTask<BigInteger>(new RandomPrimeSearch(512)); //需要利用泛型模板返回处理结果
new Thread(task).start( );
BigInteger result = task.get( ); //此处会自动判断是否执行结束,未结束会继续等待。也可以设定最大等待时间
//为了提高效率 也可以使用service方式调用
ExecutorService service = Executors.newFixedThreadPool(5);
Future<BigInteger> prime1 = service.submit(new RandomPrimeSearch(512));
这样一来,使用这种并发模式处理代码就容易多了。程序可以简单的修改为这样的结构
remoateCall1 = doRemoteCall1(); // 启动线程1 将方法处理以封装到线程中
remoateCall2 = doRemoteCall2(); // 启动线程2
remoteCall1.get().getUserSetting() ;
remoteCall2.get().getDictionaryList();
....
呵呵,基本和顺序执行时差不多,代码修改量比较小。
java 5中引入了线程执行服务ExecutorService 的概念以后, 如果以线程池的方式执行线程调用,资源消耗会大幅度减少,所以不用太担心并发处理会产生过多的系统负荷。