`

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

    博客分类:
  • JESE
阅读更多

线程池是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 will reuse previously constructed threads when they are available. These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks. Calls to execute will reuse previously constructed threads if available. If no existing thread is available, a new thread will be created and added to the pool. Threads that have not been used for sixty seconds are terminated and removed from the cache. Thus, a pool that remains idle for long enough will not consume any resources. Note that pools with similar properties but different details (for example, timeout parameters) may be created using 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) {
    }
    
}

测试结果如图:

当线程数达到5592是,只有在任务管理器Kill该进程了。

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

分享到:
评论

相关推荐

    多线程发邮件

    在IT行业中,多线程技术是一项重要的编程技巧,特别是在处理并发任务时,它能显著提升程序的执行效率。本示例“多线程发邮件”就是利用了这一特性,通过并发执行多个邮件发送任务,来加快邮件的发送速度。下面我们将...

    java多线程编程

    Java通过`Thread`类和`Runnable`接口提供了对多线程的支持。创建线程有两种方式:继承`Thread`类或实现`Runnable`接口。后者更常用,因为它可以避免单继承的限制,并且线程与业务逻辑解耦。 多线程设计模式包括但不...

    人工智能-项目实践-多线程-多线程与高并发.zip

    在Java中,Java并发工具包(JUC,Java Concurrency Utilities)是处理多线程问题的核心组件,提供了丰富的类和接口,如`Thread`、`ExecutorService`、`Semaphore`、`CountDownLatch`、`CyclicBarrier`等,这些工具...

    java多线程面试题59题集合

    以下是对Java多线程面试题59题集合中可能涉及的一些关键知识点的详细解析。 1. **线程的创建方式** - 继承Thread类:创建一个新的类,该类继承自Thread类,并重写其run()方法。 - 实现Runnable接口:创建一个实现...

    Java多线程的总结

    Java提供了一些线程安全的集合类,如Vector、ConcurrentHashMap、CopyOnWriteArrayList等,它们内部已经实现了线程同步,可以避免在多线程环境下出现数据一致性问题。 通过以上内容,初学者可以对Java多线程有一个...

    火山安卓多线程技术源码.rar

    在安卓开发中,多线程技术是至关重要的,它能够帮助...综上所述,"火山安卓多线程技术源码"可能包含了以上各种技术的实现,通过研究这些源码,开发者可以深入理解Android平台上的多线程编程,提高应用性能和用户体验。

    Android多线程

    本文将详细讲解Android中的多线程,包括AsyncTask、Thread、ExecutorService(FixedThreadPool、CachedThreadPool、SingleThreadExecutor)以及如何在主线程中安全地更新UI。 首先,Android应用的主线程,也被称为...

    java多线程编程(带目录)

    Java多线程编程是Java开发中的...在"Java多线程编程核心技术_完整版.pdf"这本书中,可能涵盖了上述所有内容,深入探讨了Java多线程编程的各个方面,包括理论、实践案例和最佳实践,帮助开发者熟练掌握这一重要技术。

    Java多线程与并发库高级应用

    在深入探讨Java多线程与并发库的高级应用之前,我们首先需要回顾一下Java多线程的基础概念和技术要点。 ##### 1.1 线程的概念 在计算机科学中,线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是...

    关于线程(java)两天的课件

    此外,死锁是多线程编程中常见的问题,当两个或更多的线程互相等待对方释放资源时就会发生。避免死锁的关键在于遵循死锁预防的四个条件:互斥、请求与保持、不剥夺和循环等待。Java提供了一些工具如jstack和VisualVM...

    java多线程设计模式

    通过深入学习和实践这些Java多线程设计模式,开发者可以更好地理解和掌握多线程环境下的编程技巧,提高程序的并发性能和稳定性。在实际开发中,应根据具体需求选择合适的设计模式和并发工具,确保代码的正确性和高效...

    Java多线程(Synchronized+Volatile+JUC 并发工具原理+线程状态+CAS+线程池)

    Java 多线程(Synchronized+Volatile+JUC 并发工具原理+线程状态+CAS+线程池) Java 多线程是 Java 语言中的一...但是,多线程编程也存在一些挑战,如线程安全、死锁、饥饿等问题,因此需要小心设计和实施多线程程序。

    Java多线程编程学习.pdf

    本篇文章将深入探讨Java中的多线程编程,包括基本概念、实现方式以及常见问题和解决方案。 一、多线程基础 1.1 线程与进程 线程是操作系统分配CPU时间的基本单位,而进程则是一个独立的执行实体,拥有自己的内存...

    va多线程开发技巧.doc

    Java多线程开发是Java开发中的重要组成部分,尤其在高并发场景下,合理地使用多线程技术可以显著提升程序的执行效率。然而,多线程编程也伴随着一系列挑战,如资源管理、线程安全、任务调度等。以下是对标题和描述中...

    多线程模型在 Socket 编程中的应用

    4. **简单多线程模型** 虽然可以并行处理多个连接,但随着连接数增加,创建和销毁线程的开销也会增大,可能造成服务器性能下降。 5. **线程池模型** 线程池提供了一种线程复用机制,固定数量的线程等待任务,如...

    【面试资料】-(机构内训资料)java面试题_多线程(68题).zip

    Java多线程是Java编程中的核心概念,尤其在面试中,它...通过解答这些题目,开发者能深入理解Java多线程的原理,并具备解决实际问题的能力。对于准备Java面试或提升编程能力的人员来说,这68道题无疑是极好的学习材料。

    Java线程 高级使用

    通过以上内容的学习,读者可以深入了解Java线程的高级使用方法,掌握如何在Java程序中高效地管理和控制线程,以及如何解决多线程环境下常见的问题。这对于开发高性能、高可用性的Java应用至关重要。

    多线程与线程池技术详解

    总之,理解和掌握多线程与线程池技术是Java程序员必备的技能,这不仅对面试有帮助,更是提升实际项目性能的关键。通过深入学习这些技术,可以更好地应对高并发环境下的挑战,为软件开发带来更高的效率和可靠性。

    JavaSE常见面试题-线程篇.pdf

    4. **线程安全**:线程安全意味着在多线程环境下,一段代码的执行不会导致数据不一致或异常。例如,Vector类是线程安全的,因为它使用了同步方法,而ArrayList则不是。 5. **线程池**:线程池是一种预先创建的线程...

    Android开发经验谈:并发编程(线程与线程池)(推荐)

    为了解决这些问题,我们需要对线程进行控制和管理,这就需要使用线程池。 线程池顾名思义,就是放线程的大池子。它可以帮助我们管理线程,避免线程不可控和内存泄漏的问题。Android中有多种类型的线程池,如...

Global site tag (gtag.js) - Google Analytics