线程池工作队列饱和策略

转载自http://blog.csdn.net/lixwjava/article/details/51813032


近段时间在看《Java并发编程实战》,第一遍每天看一章也没敲代码,并没吸收多少。看第二遍的时候压下速度,并敲代码,感觉理解深刻好多,废话止于此。


Java线程池会将提交的任务先置于工作队列中,在从工作队列中获取(SynchronousQueue直接由生产者提交给工作线程)。那么工作队列就有两种实现策略:无界队列和有界队列。无界队列不存在饱和的问题,但是其问题是当请求持续高负载的话,任务会无脑的加入工作队列,那么很可能导致内存等资源溢出或者耗尽。而有界队列不会带来高负载导致的内存耗尽的问题,但是有引发工作队列已满情况下,新提交的任务如何管理的难题,这就是线程池工作队列饱和策略要解决的问题。

饱和策略分为:Abort 策略, CallerRuns 策略,Discard策略,DiscardOlds策略。

为了更好的理解,我编写一个小的例子。

[java]  view plain  copy
 
  1. package concurrency.pool;  
  2.   
  3. import java.util.concurrent.LinkedBlockingDeque;  
  4. import java.util.concurrent.RejectedExecutionHandler;  
  5. import java.util.concurrent.ThreadPoolExecutor;  
  6. import java.util.concurrent.TimeUnit;  
  7.   
  8. /** 
  9.  * Created by li on 2016/7/2. 
  10.  */  
  11. public class SaturationPolicy {  
  12.   
  13.     /** 
  14.      * 线程池工作队列已满时,在不同饱和策略下表现 
  15.      * @param handler 线程池工作队列饱和策略 
  16.      */  
  17.     public static void policy(RejectedExecutionHandler handler){  
  18.         //基本线程2个,最大线程数为3,工作队列容量为5  
  19.         ThreadPoolExecutor exec = new ThreadPoolExecutor(2,3,0l, TimeUnit.MILLISECONDS,new LinkedBlockingDeque<>(5));  
  20.         if (handler != null){  
  21.             exec.setRejectedExecutionHandler(handler);//设置饱和策略  
  22.         }  
  23.         for (int i = 0; i < 10; i++) {  
  24.             exec.submit(new Task());//提交任务  
  25.         }  
  26.         exec.shutdown();  
  27.     }  
  28.   
  29.     public static void main(String[] args) {  
  30. //        policy(new ThreadPoolExecutor.AbortPolicy());  
  31. //        policy((new ThreadPoolExecutor.CallerRunsPolicy()));  
  32. //        policy(new ThreadPoolExecutor.DiscardPolicy());  
  33. //        policy(new ThreadPoolExecutor.DiscardOldestPolicy());  
  34.     }  
  35.   
  36.     //自定义任务  
  37.     static class Task implements Runnable {  
  38.         private static int count = 0;  
  39.         private int id = 0;//任务标识  
  40.         public Task() {  
  41.             id = ++count;  
  42.         }  
  43.         @Override  
  44.         public  void run() {  
  45.             try {  
  46.                 TimeUnit.SECONDS.sleep(3);//休眠3秒  
  47.             } catch (InterruptedException e) {  
  48.                 System.err.println("线程被中断" + e.getMessage());  
  49.             }  
  50.             System.out.println(" 任务:" + id + "\t 工作线程: "+ Thread.currentThread().getName() + " 执行完毕");  
  51.         }  
  52.     }  
  53.   
  54. }  

当工作队列满了,不同策略的处理方式为:

1.Abort策略:默认策略,新任务提交时直接抛出未检查的异常RejectedExecutionException,该异常可由调用者捕获。

 在主函数中添加如下代码:

[java]  view plain  copy
 
  1. policy(new ThreadPoolExecutor.AbortPolicy());  
运行结果为:

线程池工作队列饱和策略_第1张图片
程序抛出了RejectedExecutionException,并且一共运行了8个任务(线程池开始能运行3个任务,工作队列中存储5个队列)。当工作队列满了的时候,直接抛出了异常,而且JVM一直不退出(我现在也不知道什么原因)。我们可以看到执行任务的线程全是线程池中的线程。

2.CallerRuns策略:为调节机制,既不抛弃任务也不抛出异常,而是将某些任务回退到调用者。不会在线程池的线程中执行新的任务,而是在调用exector的线程中运行新的任务。

在主函数运行:

[java]  view plain  copy
 
  1. policy((new ThreadPoolExecutor.CallerRunsPolicy()));  
运行结果

线程池工作队列饱和策略_第2张图片

所有的任务都被运行,且有2(10 - 3 -5)个任务是在main线程中执行成功的,8个任务在线程池中的线程执行的。
3.Discard策略:新提交的任务被抛弃。

在main函数中运行

[java]  view plain  copy
 
  1. policy(new ThreadPoolExecutor.DiscardPolicy());  

线程池工作队列饱和策略_第3张图片

通过上面的结果可以显示:没有异常抛出,后面提交的2个新任务被抛弃,只处理了前8(3+5)个任务,JVM退出。

4.DiscardOldest策略:队列的是“队头”的任务,然后尝试提交新的任务。(不适合工作队列为优先队列场景)

在main函数中运行如下方法

[java]  view plain  copy
 
  1. policy(new ThreadPoolExecutor.DiscardOldestPolicy());  

运行结果:一共运行8个任务,程序结束,后面添加的任务9,任务10被执行了,而前面的任务3,任务4被丢弃。

线程池工作队列饱和策略_第4张图片



你可能感兴趣的:(并发编程,Java)