線程池的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());
        }
    }
}

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章