@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);

在多线程大师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(243,
                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