您的位置:首页 > 理论基础 > 计算机网络

项目体验:高并发httpclient和线程池的正确使用

2015-12-01 14:02 537 查看
这段时间以来都在同事在研发一个公司的新项目,其中使用到了一个技术,由于不熟悉而导致了一些性能上的问题。现简略作一下总结。

1、ExecutorService

用这个java提供的线程池机制还是很方便的,比自己写的池好得多。一般都会使用 ExecutorService normalExService = Executors.newFixedThreadPool(30) 来指定一个有30个固定大小线程的池。这只是新建了个池子,只有往里面放入thread类对象时才会运行。方法是调用

exService.execute(msgThread)

execute()相当于把一个继承了Thread或实现了Runnable接口的线程类当成一个任务加入到pool中。一个task一旦被加入到了pool,则由空闲的thread去竞争获取并执行。

如果execute了10次,怎么判断这10个task都完成了呢。

while (!exService.isTerminated()) {

// 执行中

}

可以用一个while,用exService.isTerminated()来判断是否所有的task都完成。嗯。。这些都不是要说的问题。试想一下。如果一直不停地往pool中加入task,但是task的处理速度远跟不上加入的速度,但造成pool积压严重。其实如果只一个空的task,也不会有多大问题。用JProfiler看了一下JVM,发现40w个task,本身也只有10m左右。

但是,如果task中又包含了很多对象,如字符串,map,list等等,在生成task时也会把这些东西一起生成,即使加了pool中,又不会马上执行到,只能是空站着耗内存罢了。

所以程序运行一段时间后,JVM越来越小,十几分钟后就达到了最大值,此时程序也越来越慢。最后打印出 heap outofmenery就挂了。另外说一点。一般控制台只是会说内存溢出,但是具体是什么东西占了空间是无法得知了,所以必须要有文件可以看。

在Tomcat的catalina第一行配置 set J***A_OPTS=%J***A_OPTS% -server -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\heapdump

就会在程序因为内存溢出崩掉了输出一个记录了崩溃时JVM和程序的具体信息,很有帮助,就像飞机的黑匣子一样。

既然不能一次性把task全加入到pool中,那只能等到pool中有空闲线程时再join。这样pool只也许只会有30个(配的30)task。组内的王大师利用星期天的时间,边抱着孩子边写设计了一个算法,解决了上述问题。

不用execute了,用submit

List<Future> futures = new ArrayList<Future>();
ExecutorService pool = Executors. newFixedThreadPool(poolSize);
		Future future = null;
		List<Future> tempFutures = null;
		while(true) {
			//判断队列是否有数据
			if(queue.size() > 0) {
				if(futures.size() == poolSize) {  //------------------------------1
					tempFutures = new ArrayList<Future>();
					for(int i = 0; i < poolSize; i++ ) {
						future = futures.get(i);
						if(! future.isDone()) {
							tempFutures.add(future);
						}
					}
					futures = tempFutures;
				}
				else { // ----------------------------------------2
					BizBean biz = new BizBean();
					biz.initBean((String) queue.poll());
					future = pool.submit(biz); // 
					futures.add(future);
				}
			} 
		}

submit():Submits a Runnable task for execution and returns a Future representing that task。Future代表了当前的task。在这里我定义了一个List<Future>,用来存30个task的状态。程序进来时,前30次首先走到2中,此时已经向pool中提交了30个task并已经run了。第31次进来时,走入到1中,判断前30个task是否做完。! future.isDone()表示没有做完的放到一个临时的list,循环完后把临时list传递给futures,下一次再进1时,就可以继续判断上一次没有做完的这次做完了没有。futures代表了最新的task的状态。

使用了这个后,task实例就一直维持在一个合理的数量,相关的类、对象也不会创建,不会再像使用execute时一股脑地全部join到pool中空占内存了。

htticlient请见下篇blog




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: