JAVA|项目体验(高并发httpclient和线程池的正确使用)

这段时间以来都在同事在研发一个公司的新项目,其中使用到了一个技术,由于不熟悉而导致了一些性能上的问题。现简略作一下总结。
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中,又不会马上执行到,只能是空站着耗内存罢了。
【JAVA|项目体验(高并发httpclient和线程池的正确使用)】所以程序运行一段时间后,JVM越来越小,十几分钟后就达到了最大值,此时程序也越来越慢。最后打印出 heap outofmenery就挂了。另外说一点。一般控制台只是会说内存溢出,但是具体是什么东西占了空间是无法得知了,所以必须要有文件可以看。
在Tomcat的catalina第一行配置 set JAVA_OPTS=%JAVA_OPTS% -server -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\heapdump
就会在程序因为内存溢出崩掉了输出一个记录了崩溃时JVM和程序的具体信息,很有帮助,就像飞机的黑匣子一样。
既然不能一次性把task全加入到pool中,那只能等到pool中有空闲线程时再join。这样pool只也许只会有30个(配的30)task。组内的王大师利用星期天的时间,边抱着孩子边写设计了一个算法,解决了上述问题。
不用execute了,用submit

List futures = new ArrayList();

ExecutorService pool = Executors. newFixedThreadPool(poolSize); Future future = null; List tempFutures = null; while(true) { //判断队列是否有数据 if(queue.size() > 0) { if(futures.size() == poolSize) {//------------------------------1 tempFutures = new ArrayList(); 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,用来存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



??
??

    推荐阅读