1. 程式人生 > >執行緒池的RejectedExecutionHandler(拒絕策略)

執行緒池的RejectedExecutionHandler(拒絕策略)

JAVA為多執行緒場景提供了執行緒池,下面是一個執行緒池的構造方法:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

其中這些引數的使用和說明在我的一篇文章中已經有了介紹,如果不太清楚的可以參考這篇文章:http://blog.csdn.net/jgteng/article/details/54409887

這裡想對拒絕策略RejectedExecutionHandler做一下詳細的介紹。

在使用執行緒池並且使用有界佇列的時候,如果佇列滿了,任務新增到執行緒池的時候就會有問題,針對這些問題java執行緒池提供了以下幾種策略:

  • AbortPolicy
  • DiscardPolicy
  • DiscardOldestPolicy
  • CallerRunsPolicy
  • 自定義
◇AbortPolicy 該策略是執行緒池的預設策略。使用該策略時,如果執行緒池佇列滿了丟掉這個任務並且丟擲RejectedExecutionException異常。 原始碼如下:
 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            //不做任何處理,直接丟擲異常
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
◇DiscardPolicy 這個策略和AbortPolicy的slient版本,如果執行緒池佇列滿了,會直接丟掉這個任務並且不會有任何異常。 原始碼如下:
   public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        	//就是一個空的方法
        }

◇DiscardOldestPolicy 這個策略從字面上也很好理解,丟棄最老的。也就是說如果佇列滿了,會將最早進入佇列的任務刪掉騰出空間,再嘗試加入佇列。 因為佇列是隊尾進,隊頭出,所以隊頭元素是最老的,因此每次都是移除對頭元素後再嘗試入隊。 原始碼如下:
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
            	//移除隊頭元素
                e.getQueue().poll();
                //再嘗試入隊
                e.execute(r);
            }
        }

CallerRunsPolicy 使用此策略,如果新增到執行緒池失敗,那麼主執行緒會自己去執行該任務,不會等待執行緒池中的執行緒去執行。就像是個急脾氣的人,我等不到別人來做這件事就乾脆自己幹。 原始碼如下:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                //直接執行run方法
                r.run();
            }
        }

自定義 如果以上策略都不符合業務場景,那麼可以自己定義一個拒絕策略,只要實現RejectedExecutionHandler介面,並且實現rejectedExecution方法就可以了。具體的邏輯就在rejectedExecution方法裡去定義就OK了。 例如:我定義了我的一個拒絕策略,叫做MyRejectPolicy,裡面的邏輯就是列印處理被拒絕的任務內容
public class MyRejectPolicy implements RejectedExecutionHandler{
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        //Sender是我的Runnable類,裡面有message欄位
        if (r instanceof Sender) {
            Sender sender = (Sender) r;
            //直接列印
            System.out.println(sender.getMessage());
        }
    }
}

這幾種策略沒有好壞之分,只是適用不同場景,具體哪種合適根據具體場景和業務需要選擇,如果需要特殊處理就自己定義好了。