首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > Ruby Rails >

深入浅出多线程(4)对CachedThreadPool OutOfMemoryError有关问题的一些想法

2012-10-28 
深入浅出多线程(4)对CachedThreadPool OutOfMemoryError问题的一些想法线程池是Conncurrent包提供给我们的

深入浅出多线程(4)对CachedThreadPool OutOfMemoryError问题的一些想法

线程池是Conncurrent包提供给我们的一个重要的礼物。使得我们没有必要维护自个实现的心里很没底的线程池了。但如何充分利用好这些线程池来加快我们开发与测试效率呢?当然是知己知彼。本系列就说说对CachedThreadPool使用的一下问题。
??? 下面是对CachedThreadPool的一个测试,程序有问题吗?

package?net.blogjava.vincent;

import?java.util.concurrent.BlockingQueue;
import?java.util.concurrent.ExecutorService;
import?java.util.concurrent.Executors;
import?java.util.concurrent.FutureTask;
import?java.util.concurrent.ThreadPoolExecutor;
import?java.util.concurrent.TimeUnit;

public?class?CachedThreadPoolIssue?{

????/**
?????*?@param?args
?????*/
????public?static?void?main(String[]?args)?{
????????
????????ExecutorService?es?=?Executors.newCachedThreadPool();
????????for(int?i?=?1;?i<8000;?i++)
????????????es.submit(new?task());

????}

}
class?task?implements?Runnable{

????@Override
????public?void?run()?{
????try?{
????????Thread.sleep(4000);
????}?catch?(InterruptedException?e)?{
????????//?TODO?Auto-generated?catch?block
????????e.printStackTrace();
????}
????????
????}
????
}

如果对JVM没有特殊的设置,并在Window平台上,那么就会有一下异常的发生:
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
?? ?at java.lang.Thread.start0(Native Method)
?? ?at java.lang.Thread.start(Unknown Source)
?? ?at java.util.concurrent.ThreadPoolExecutor.addIfUnderMaximumPoolSize(Unknown Source)
?? ?at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)
?? ?at java.util.concurrent.AbstractExecutorService.submit(Unknown Source)
?? ?at net.blogjava.vincent.CachedThreadPoolIssue.main(CachedThreadPoolIssue.java:19)
看看Doc对该线程池的介绍:
Creates a thread pool that creates new threads as needed, but willreuse previously constructed threads when they are available. Thesepools will typically improve the performance of programs that executemany short-lived asynchronous tasks. Calls to execute will reusepreviously constructed threads if available. If no existing thread isavailable, a new thread will be created and added to the pool. Threadsthat have not been used for sixty seconds are terminated and removedfrom the cache. Thus, a pool that remains idle for long enough will notconsume any resources. Note that pools with similar properties butdifferent details (for example, timeout parameters) may be createdusing ThreadPoolExecutor constructors.

有以下几点需要注意:
1. 指出会重用先前的线程,不错。
2. 提高了短Task的吞吐量。
3. 线程如果60s没有使用就会移除出Cache。

好像跟刚才的错误没有关系,其实就第一句话说了问题,它会按需要创建新的线程,上面的例子一下提交8000个Task,意味着该线程池就会创建8000线程,当然,这远远高于JVM限制了。
注:在JDK1.5中,默认每个线程使用1M内存,8000M !!! 可能吗!!

所以我感觉这应该是我遇到的第一个Concurrent不足之处,既然这么设计,那么就应该在中Doc指出,应该在使用避免大量Task提交到给CachedThreadPool.
可能读者不相信,那么下面的例子说明了他创建的Thread。
在ThreadPoolExecutor提供的API中,看到它提供beforeExecute和afterExecute两个可以在子类中重载的方法,该方法在线程池中线程执行Task之前与之后调用。所以我们在beforeExexute中查看目前线程编号就可以确定目前的线程数目.

package?net.blogjava.vincent;

import?java.util.concurrent.BlockingQueue;
import?java.util.concurrent.ExecutorService;
import?java.util.concurrent.Executors;
import?java.util.concurrent.FutureTask;
import?java.util.concurrent.SynchronousQueue;
import?java.util.concurrent.ThreadPoolExecutor;
import?java.util.concurrent.TimeUnit;

public?class?CachedThreadPoolIssue?{

????/**
?????*?@param?args
?????*/
????public?static?void?main(String[]?args)?{
????????
????????ExecutorService?es?=?new?LogThreadPoolExecutor(0,?Integer.MAX_VALUE,
????????????????60L,?TimeUnit.SECONDS,
????????????????new?SynchronousQueue<Runnable>());
????????for(int?i?=?1;?i<8000;?i++)
????????????es.submit(new?task());

????}

}
class?task?implements?Runnable{

????@Override
????public?void?run()?{
????try?{
????????Thread.sleep(600000);
????}?catch?(InterruptedException?e)?{
????????//?TODO?Auto-generated?catch?block
????????e.printStackTrace();
????}
????????
????}
????
}
class?LogThreadPoolExecutor?extends?ThreadPoolExecutor{

????public?LogThreadPoolExecutor(int?corePoolSize,?int?maximumPoolSize,
????????????long?keepAliveTime,?TimeUnit?unit,?BlockingQueue<Runnable>?workQueue)?{
????????super(corePoolSize,?maximumPoolSize,?keepAliveTime,?unit,?workQueue);
????}
????protected?void?beforeExecute(Thread?t,?Runnable?r)?{?
????????System.out.println(t.getName());
????}
????protected?void?afterExecute(Runnable?r,?Throwable?t)?{
????}
????
}

测试结果如图:
深入浅出多线程(4)对CachedThreadPool OutOfMemoryError有关问题的一些想法
当线程数达到5592是,只有在任务管理器Kill该进程了。

如何解决该问题呢,其实在刚才实例化时就看出来了,只需将
new LogThreadPoolExecutor(0, Integer.MAX_VALUE,
??????????????? 60L, TimeUnit.SECONDS,
??????????????? new SynchronousQueue<Runnable>());
Integer.MAX_VALUE 改为合适的大小。对于该参数的含义,涉及到线程池的实现,将会在下个系列中指出。
当然,其他的解决方案就是控制Task的提交速率,避免超过其最大限制。

热点排行