`
talentluke
  • 浏览: 604886 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用简介

 
阅读更多
在多线程大师Doug Lea的贡献下,在JDK1.5中加入了许多对并发特性的支持,例如:线程池。
一、简介
线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为:
ThreadPoolExecutor(int  corePoolSize,  int  maximumPoolSize, 
                
long  keepAliveTime, TimeUnit unit, 
                BlockingQueue workQueue, 
                RejectedExecutionHandler handler)

    corePoolSize:   线程池维护线程的最少数量,   
    maximumPoolSize:线程池维护线程的最大数量
    keepAliveTime:  线程池维护线程所允许的空闲时间
    unit:           线程池维护线程所允许的空闲时间的单位
    workQueue:      线程池所使用的缓冲队列
    handler:        线程池对拒绝任务的处理策略


   一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。

    当一个任务通过execute(Runnable)方法欲添加到线程池时:
    如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
    如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
    如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
    如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。 
    也就是:处理任务的优先级为:
    核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
    当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

    unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:
             NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。

    workQueue我常用的是:java.util.concurrent.ArrayBlockingQueue
    
    handler有四个选择:
    ThreadPoolExecutor.AbortPolicy()        
       抛出java.util.concurrent.RejectedExecutionException异常
    ThreadPoolExecutor.CallerRunsPolicy()   
       重试添加当前的任务,他会自动重复调用execute()方法
    ThreadPoolExecutor.DiscardOldestPolicy()
       抛弃旧的任务
    ThreadPoolExecutor.DiscardPolicy()      
       抛弃当前的任务


import  java.io.Serializable;
import  java.util.concurrent.ArrayBlockingQueue;
import  java.util.concurrent.ThreadPoolExecutor;
import  java.util.concurrent.TimeUnit;

public   class  TestThreadPool {

    
private   static   int  produceTaskSleepTime  =   2 ;
    
private   static   int  consumeTaskSleepTime  =   2000 ;
    
private   static   int  produceTaskMaxNumber  =   10 ;
    
 
public   static   void  main(String[] args) {

        
// 构造一个线程池    
        ThreadPoolExecutor threadPool  =   new  ThreadPoolExecutor( 2 4 3 ,
                TimeUnit.SECONDS, 
new  ArrayBlockingQueue( 3 ),
                
new  ThreadPoolExecutor.DiscardOldestPolicy());

        
for ( int  i = 1 ;i <= produceTaskMaxNumber;i ++ ){
            
try  {               
                
// 产生一个任务,并将其加入到线程池
                String task  =   " task@  "   +  i;
    System.out.println(
" put  "   +  task);
    threadPool.execute(
new  ThreadPoolTask(task));
    
    
// 便于观察,等待一段时间
                Thread.sleep(produceTaskSleepTime);
            } 
catch  (Exception e) {
                e.printStackTrace();
            }
        }
 }

 
/**
  * 线程池执行的任务
  * 
@author  hdpan
  
*/
    
public   static   class  ThreadPoolTask  implements  Runnable,Serializable{
     
private   static   final   long  serialVersionUID  =   0 ;
     
// 保存任务所需要的数据
         private  Object threadPoolTaskData;
        
        ThreadPoolTask(Object tasks){
            
this .threadPoolTaskData  =  tasks;
        }
        
public   void  run(){
            
// 处理一个任务,这里的处理方式太简单了,仅仅是一个打印语句
   System.out.println( " start .. " + threadPoolTaskData);
         
try  {
             
//// 便于观察,等待一段时间
                Thread.sleep(consumeTaskSleepTime);
            } 
catch  (Exception e) {
                e.printStackTrace();
            }
            threadPoolTaskData 
=   null ;
        }
        
public  Object getTask(){
            
return   this .threadPoolTaskData;
        }
    }
}
说明:
1、在这段程序中,一个任务就是一个Runnable类型的对象,也就是一个ThreadPoolTask类型的对象。
2、一般来说任务除了处理方式外,还需要处理的数据,处理的数据通过构造方法传给任务。
3、在这段程序中,main()方法相当于一个残忍的领导,他派发出许多任务,丢给一个叫 threadPool的任劳任怨的小组来做。
    这个小组里面队员至少有两个,如果他们两个忙不过来, 任务就被放到任务列表里面。
    如果积压的任务过多,多到任务列表都装不下(超过3个)的时候,就雇佣新的队员来帮忙。但是基于成本的考虑,不能雇佣太多的队员, 至多只能雇佣 4个。
    如果四个队员都在忙时,再有新的任务, 这个小组就处理不了了,任务就会被通过一种策略来处理,我们的处理方式是不停的派发, 直到接受这个任务为止(更残忍!呵呵)。
    因为队员工作是需要成本的,如果工作很闲,闲到 3SECONDS都没有新的任务了,那么有的队员就会被解雇了,但是,为了小组的正常运转,即使工作再闲,小组的队员也不能少于两个。

4、通过调整 produceTaskSleepTime和 consumeTaskSleepTime的大小来实现对派发任务和处理任务的速度的控制, 改变这两个值就可以观察不同速率下程序的工作情况。
5、通过调整4中所指的数据,再加上调整任务丢弃策略, 换上其他三种策略,就可以看出不同策略下的不同处理方式。
6、对于其他的使用方法,参看jdk的帮助,很容易理解和使用。

发邮件示例:
在普通的web应用中,发送邮件应该只能算小任务,而使用jms来发送邮件有 点杀鸡用牛刀的味道,那么如果能建立一个线程池来管理这些小线程并重复使用他们,应该来说是一个简单有效的方案,我们可以使用concurrent包中的 Executors来建立线程池,Executors是一个工厂,也是一个工具类,我把它的api的介绍简单的翻译了一下(如果翻译有误请大家不要吝啬手 中的砖头)
/**  
 * 由spring管理的线程池类,返回的ExecutorService就是给我们来执行线程的 
*如果不交给spring管理也是可以的,可以使用单例模式来实现同样功能,但是poolSize   *要hardcode了 
 * 
@author  张荣华(ahuaxuan) 
@version  $Id$ 
 
*/   
public   class  EasyMailExecutorPool  implements  InitializingBean {  
  
  
// 线程池大小,spring配置文件中配置  
   private   int  poolSize;  
  
private  ExecutorService service;  
  
  
public  ExecutorService getService() {  
    
return  service;  
  }  
  
  
public   int  getPoolSize() {  
    
return  poolSize;  
  }  
  
  
public   void  setPoolSize( int  poolSize) {  
    
this .poolSize  =  poolSize;  
  }  
  
  
/**  
   * 在 bean 被初始化成功之后初始化线程池大小 
   
*/   
  
public   void  afterPropertiesSet()  throws  Exception {  
    service 
=  Executors.newFixedThreadPool(poolSize);  
  }  
}  

这样我们就初始化了线程池的大小,接下来就是如何使用这个线程池中的线程了,我们看看MailService是如何来使用线程池中的线程的,这个类中的代码我已经作了详细的解释 
代码
/**  
 * 用来发送 mail 的 service, 其中有一个内部类专门用来供线程使用 
 * 
@author  张荣华(ahuaxuan) 
 * 
@since  2007-7-11 
 * 
@version  $Id$ 
 
*/   
public   class  EasyMailServieImpl  implements  EasyMailService{  
  
private   static   transient  Log logger  =  LogFactory.getLog(EasyMailServieImpl. class );   
    
  
// 注入MailSender  
   private  JavaMailSender javaMailSender;  
    
  
// 注入线程池  
   private  EasyMailExecutorPool easyMailExecutorPool;  
    
  
// 设置发件人  
   private  String from;  
    
  
public   void  setEasyMailExecutorPool(EasyMailExecutorPool easyMailExecutorPool) {  
    
this .easyMailExecutorPool  =  easyMailExecutorPool;  
  }  
  
  
public   void  setJavaMailSender(JavaMailSender javaMailSender) {  
    
this .javaMailSender  =  javaMailSender;  
  }  
    
  
public   void  setFrom(String from) {  
    
this .from  =  from;  
  }  
  
  
/**  
   * 简单的邮件发送接口,感兴趣的同学可以在这个基础上继续添加 
   * 
@param  to 
   * 
@param  subject 
   * 
@param  text 
   
*/   
  
public   void  sendMessage(EmailEntity email){  
    
if  ( null   ==  email) {  
      
if  (logger.isDebugEnabled()) {  
        logger.debug(
" something you need to tell here " );  
      }  
      
return ;  
    }  
    SimpleMailMessage simpleMailMessage 
=   new  SimpleMailMessage();  
      
    simpleMailMessage.setTo(email.getTo());  
    simpleMailMessage.setSubject(email.getSubject());  
    simpleMailMessage.setText(email.getText());  
    simpleMailMessage.setFrom(from);  
      
    easyMailExecutorPool.getService().execute(
new  MailRunner(simpleMailMessage));  
  }  
    
  
/**  
   * 发送复杂格式邮件的接口,可以添加附件,图片,等等,但是需要修改这个方法, 
   * 如何做到添加附件和图片论坛上有例子了,需要的同学搜一下, 
   * 事实上这里的text参数最好是来自于模板,用模板生成html页面,然后交给javamail去发送, 
   * 如何使用模板来生成html见 {
@link   http://www.iteye.com/topic/71430  } 
   *  
   * 
@param  to 
   * 
@param  subject 
   * 
@param  text 
   * 
@throws  MessagingException 
   
*/   
  
public   void  sendMimeMessage(EmailEntity email)  throws  MessagingException {  
    
if  ( null   ==  email) {  
      
if  (logger.isDebugEnabled()) {  
        logger.debug(
" something you need to tell here " );  
      }  
      
return ;  
    }  
    MimeMessage message 
=  javaMailSender.createMimeMessage();  
    MimeMessageHelper helper 
=   new  MimeMessageHelper(message);  
      
    helper.setTo(email.getTo());  
    helper.setFrom(from);  
    helper.setSubject(email.getSubject());  
      
    
this .addAttachmentOrImg(helper, email.getAttachment(),  true );  
    
this .addAttachmentOrImg(helper, email.getImg(),  false );  
      
    
// 这里的text是html格式的, 可以使用模板引擎来生成html模板, velocity或者freemarker都可以做到  
    helper.setText(email.getText(), true );  
      
    easyMailExecutorPool.getService().execute(
new  MailRunner(message));  
  }  
    
  
/**  
   * 添加附件或者是图片 
   * 
@param  helper 
   * 
@param  map 
   * 
@param  isAttachment 
   * 
@throws  MessagingException 
   
*/   
  
private   void  addAttachmentOrImg(MimeMessageHelper helper, Map map,  boolean  isAttachment)  throws  MessagingException {  
    
for  (Iterator it  =  map.entrySet().iterator(); it.hasNext();) {  
      Map.Entry entry 
=  (Map.Entry) it.next();  
      String key 
=  (String) entry.getKey();  
      String value 
=  (String) entry.getValue();  
      
if  (StringUtils.isNotBlank(key)  &&  StringUtils.isNotBlank(value)) {  
        FileSystemResource file 
=   new  FileSystemResource( new  File(value));  
        
if  ( ! file.exists())  continue ;  
        
if  (isAttachment) {  
          helper.addAttachment(key, file);  
        } 
else  {  
          helper.addInline(key, file);  
        }  
      }  
    }  
  }  
    
  
/**  
   * 用来发送邮件的 Runnable, 该类是一个内部类,之所以使用内部类,而没有使用嵌套类(静态内部类), 
   * 是因为内部类可以之间得到 service 的 javaMailSender 
   * 每次发送邮件都会从线程池中取一个线程, 然后进行发邮件操作 
   * 
@author  ahuaxuan 
   
*/   
  
private   class  MailRunner  implements  Runnable {  
    SimpleMailMessage simpleMailMessage;  
    MimeMessage mimeMessage;   
      
    
/**  
     * 构造简单文本邮件 
     * 
@param  simpleMailMessage 
     
*/   
    
public  MailRunner(SimpleMailMessage simpleMailMessage) {  
      
if  (mimeMessage  ==   null ) {  
        
this .simpleMailMessage  =  simpleMailMessage;  
      }  
    }  
      
    
/**  
     * 构造复杂邮件,可以添加附近,图片,等等 
     * 
@param  mimeMessage 
     
*/   
    
public  MailRunner(MimeMessage mimeMessage) {  
      
if  (simpleMailMessage  ==   null ) {  
        
this .mimeMessage  =  mimeMessage;  
      }  
    }  
      
    
/**  
     * 该方法将在线程池中的线程中执行 
     
*/   
    
public   void  run() {  
      
try  {  
        
if  (simpleMailMessage  !=   null ) {  
          javaMailSender.send(
this .simpleMailMessage);  
        } 
else   if  (mimeMessage  !=   null ) {  
          javaMailSender.send(
this .mimeMessage);  
        }  
                
            } 
catch  (Exception e) {  
              
if  (logger.isDebugEnabled()) {  
                logger.debug(
" logger something here " , e);  
              }  
            }       
    }  
  }  
}  

MailService中的EmailEntity是对邮件的抽象(我只使用了 失血模型,事实上我们也可以让这个EmailEntity来实现Runnable接口,这样Service中的内部类就可以去掉了,同时service中 的大部分代码就要搬到EmailEntity及其父类里了,大家更倾向于怎么做呢?),代码如下: 
代码
/**  
 * 该类是对邮件的抽象,邮件有哪些属性,这个类就有哪些属性 显然这个只是一个例子, 
 * 这个例子中附带mimemessage发送所需的附件或者图片(如果有的话) 
 * 需要使用的同学自己扩展 
 *  
 * 
@author  张荣华(ahuaxuan) 
@version  $Id$ 
 
*/   
public   class  EmailEntity {  
  
  String to;  
  
  String subject;  
  
  String text;  
  
  
// 邮件附件  
  Map 
分享到:
评论

相关推荐

    JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用

    "JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用" JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用是Java多线程编程中的一种重要概念。随着多线程编程的普及,线程池的使用变得...

    JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用简介.doc

    JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用简介

    JDK1.5线程池源码及详细注释

    JDK 1.5引入了java.util.concurrent包,其中包含了线程池的实现,使得并发编程更加便捷和高效。线程池的核心在于它的设计策略,包括核心线程数、最大线程数、线程存活时间、工作队列以及拒绝策略。 线程池的主要类...

    jdk1.5 线程并发与线程池的使用

    在Java中,`java.util.concurrent`包中的`ThreadPoolExecutor`是线程池的主要实现。 **ThreadPoolExecutor的使用** `ThreadPoolExecutor`包含以下几个关键参数: 1. **corePoolSize**:核心线程数,即使没有任务,...

    JDK自带线程池分析

    JDK 自带线程池是 Java 语言中用于管理和执行线程的工具,旨在提高多线程编程的效率和灵活性。本文将详细介绍 JDK 自带线程池的组成、创建方法、优点和常见应用场景。 多线程技术 多线程技术是指在一个处理器单元...

    backport-util-concurrent(2.2 /3.1)

    backport-util-concurrent库,正如其名,是一种将Java 5及以上版本的并发特性“回移植”到Java 1.4及更早版本的工具,使得开发者能在较旧的Java环境中享受现代并发编程的优势。本文将深入探讨这个库的2.2和3.1两个...

    jdk1.8 rt.jar 源码

    4. **多线程**:`java.lang.Thread`和`java.util.concurrent`包中的类,如`ExecutorService`、`ThreadPoolExecutor`等,展示了如何实现并发和异步执行。 5. **反射机制**:`java.lang.reflect`包中的类,如`Class`...

    Java线程池使用说明

    在JDK 1.5版本之前,Java对线程池的支持非常有限,而在JDK 1.5之后,加入了java.util.concurrent包,其中包含了一系列关于线程池的接口和类,极大地丰富了线程池的应用场景和管理方式。 线程池的主要作用是限制系统...

    Java线程池文档

    线程池在Java中是通过`java.util.concurrent`包下的`ThreadPoolExecutor`类实现的。 线程池的主要作用是限制系统中执行线程的数量,通过预先配置好的线程数量,可以避免过多线程导致的资源浪费和系统拥挤,从而提高...

    自定义实现Java线程池1-模拟jdk线程池执行流程1

    首先,Java中的线程池设计始于JDK 5.0,主要通过`java.util.concurrent`包中的`Executor`接口实现。这个接口仅有一个`execute()`方法,用于提交执行任务。我们也将遵循这个设计,实现一个简单的线程池类`...

    java socket线程池

    Java中,线程池是通过java.util.concurrent.ThreadPoolExecutor类实现的,它为Java应用程序提供了一种有效地管理线程资源的方式。从JDK 1.5开始,Java并发API得到了增强,提供了更为强大的并发工具和库,其中就包括...

    java线程池使用说明[借鉴].pdf

    线程池的引入始于JDK 1.5,它引入了`java.util.concurrent`包,提供了`Executor`、`ExecutorService`和相关的实现类,如`ThreadPoolExecutor`和`ScheduledThreadPoolExecutor`。 线程池的主要作用是限制系统中同时...

    [Java参考文档].JDK_API中文版

    这份JDK_API中文版文档对于学习和掌握Java编程至关重要,它能帮助开发者快速查找和理解类库的使用方法,解决编程过程中遇到的问题。在实际工作中,应经常查阅和参考,以深化对Java的理解和应用。

    JAVA多线程框架.pdf

    Java.util.concurrent包是JDK5引入的一个重要更新,它包含了Doug Lea设计的并发库,极大地提升了Java处理并发的能力。这个库提供了高效且易用的多线程工具,包括线程池、同步器和定时任务管理等。 线程池是Java多...

    笔记-6、线程池1

    我们可以扩展`java.util.concurrent.ThreadPoolExecutor`类,它提供了更复杂的线程池管理功能,包括工作队列、核心线程数、最大线程数等参数。 3. JDK 中的线程池和工作机制 JDK内置了多种线程池实现,如`...

    JDK1.6 API帮助文档

    **JDK1.6 API(应用程序接口)是Java开发工具包的一个核心组成部分,它提供了大量预定义的类和方法,供开发者在构建Java应用程序时使用。API文档详细地阐述了这些类、接口、方法和构造函数的功能、用法以及参数说明...

    java线程池的使用方式

    然而,自JDK 1.5开始,随着`java.util.concurrent`包的引入,Java线程池的功能得到了极大的增强,为开发者提供了更加高效且易于管理的线程管理方式。 #### 二、线程池的作用与必要性 线程池的主要功能在于限制系统...

    java 多线程同步

    高级实用工具类是`java.util.concurrent`的核心,包括线程安全集合(如ConcurrentHashMap、CopyOnWriteArrayList等)、线程池(ExecutorService、ThreadPoolExecutor、ScheduledThreadPoolExecutor等)、信号...

    openjdk8源码

    8. **并发和多线程**:OpenJDK8的源码中,`java.util.concurrent`包下包含了丰富的并发工具类,如`ExecutorService`, `Semaphore`, `CountDownLatch`等,以及线程池的实现`ThreadPoolExecutor`。 9. **Garbage ...

    backport-util-concurrent_java_backport_源码.zip

    此外,backport-util-concurrent还实现了`ExecutorService`和`ThreadPoolExecutor`,这是Java 5中的线程池管理框架。`ExecutorService`定义了一组线程执行服务,可以管理和控制并发任务的执行。`ThreadPoolExecutor`...

Global site tag (gtag.js) - Google Analytics