利用Doug Lea的并发包实现带超时机制的线程池

    jdk5引入的concurrent包来自于Doug Lea的卓越贡献。最近我在查找服务器OOM的原因之后,决定采用这个包重写应用中一个servlet,这个servlet调用了一个阻塞方法,当被阻塞 之后,服务器中的线程数(因为阻塞了,后续请求不断地新增线程)突然增加导致了服务器当机,因此决定采用一个线程池,并且设置超时,如果阻塞方法超过一定 时间就取消线程。因为我们的项目仍然跑在jdk 1.4.2上面,短期内不可能升级到jdk5,还是要利用这个并发包。去 这里下载源码并自己打包成jar,加入项目的lib,然后利用 PooledExecutorTimedCallable来实现我们的需求。首先是线程池,相当简单:
java 代码
 
  1. import EDU.oswego.cs.dl.util.concurrent.BoundedBuffer;  
  2. import EDU.oswego.cs.dl.util.concurrent.PooledExecutor;  
  3. /** 
  4.  * 

    类说明:线程池

     
  5.  * 

    注意事项:

     
  6.  * 
     
     
  7.  * 

    创建日期:Sep 7, 2007 1:25:33 PM

     
  8. * @author:dennis zane 
  9.  * @version $Id:$ 
  10.  */  
  11. public class MyThreadPool{  
  12.     private static PooledExecutor exec = new PooledExecutor(new BoundedBuffer(  
  13.             20), 30);  
  14.     static {  
  15.         exec.setKeepAliveTime(1000 * 60 * 5);  
  16.         exec.createThreads(5);  
  17.         exec.setMinimumPoolSize(4);  
  18.     }  
  19.   
  20.     public static void execute(final Runnable r) {  
  21.         try {  
  22.             exec.execute(r);  
  23.         } catch (InterruptedException e) {  
  24.             Thread.currentThread().interrupt();  
  25.         }  
  26.     }  
  27.   
  28.     public static void shutdown() {  
  29.         exec.shutdownAfterProcessingCurrentlyQueuedTasks();  
  30.         exec = null;  
  31.     }  
  32.   
  33. }  

    静态初始化并设置一个PoolExecutor,设置空闲线程的存活时间为5分钟,设置最小线程数为4,最大线程数为30,一开始创建5个线程以待使用 (根据各自的应用调整这些参数),另外提供了shutdown方法以供ServeltContextListener的contextDestroyed 方法调用以关闭线程池。那么,结合TimedCallable来实现提交线程的超时机制,调用类似:
java 代码
 
  1. //设置超时时间  
  2.         private static final long timeout = 1000;  
  3.         ......  
  4.         ......  
  5.         Callable callable = new Callable() {  
  6.              public Object call() {  
  7.                  return new YourProgram().run();                     
  8.              }  
  9.          };  
  10.          TimedCallable timedCallable = new TimedCallable(callable, timeout);  
  11.          FutureResult future = new FutureResult();  
  12.          Runnable cmd = future.setter(timedCallable);  
  13.          //提交给线程池来执行  
  14.          MyThreadPool.execute(cmd);  
  15.          //获取任务结果  
  16.          YourObject obj= (YourObject) future.get();  
如果不是很理解这段代码,那么也许你应该先看看jdk5引入的Future、FutureTask等类,或者看看 这里的 文档。对于超时时间的大小估算,你应当在生产环境中计算该阻塞方法的调用时间,正常运行一段时间,利用脚本语言(比如ruby、python)分析日志以 得到一个调用花费时间的最大值作为timeout,这里的单位是毫秒。而线程池大小的估算,要看你提交给线程执行的任务的类型:如果是计算密集型的任务, 那么线程池的大小一般是(cpu个数+1);如果是IO密集型的任务(一般的web应用皆是此类),那么估算有一个公式,
假设N-cpu是cpu个数,U是目标CPU使用率,W/C是任务的等待时间与计算时间的比率,那么最优的池大小等于:
N-threads=N-cpu*U*(1+W/C)
   

你可能感兴趣的:(jdk,应用服务器,python,servlet,Ruby)