1. 程式人生 > 其它 >四種執行緒池拒絕策略

四種執行緒池拒絕策略

一、前言


執行緒池,相信很多人都有用過,沒用過相信的也有學習過。但是,執行緒池的拒絕策略,相信知道的人會少許多。


二、四種執行緒池拒絕策略


當執行緒池的任務快取佇列已滿並且執行緒池中的執行緒數目達到maximumPoolSize時,如果還有任務到來就會採取任務拒絕策略,通常有以下四種策略:
ThreadPoolExecutor.AbortPolicy:丟棄任務並丟擲RejectedExecutionException異常。 ThreadPoolExecutor.DiscardPolicy:丟棄任務,但是不丟擲異常。 ThreadPoolExecutor.DiscardOldestPolicy:丟棄佇列最前面的任務,然後重新提交被拒絕的任務 ThreadPoolExecutor.CallerRunsPolicy:由呼叫執行緒(提交任務的執行緒)處理該任務


三、執行緒池預設的拒絕策略


既然有四種拒絕策略可以選擇,那麼執行緒池的預設拒絕策略是什麼呢?檢視

java.util.concurrent.ThreadPoolExecutor類的原始碼,我們可以看到:

  1. /**
  2. * The default rejected execution handler
  3. */
  4. private static final RejectedExecutionHandler defaultHandler =
  5. new AbortPolicy();

執行緒池的預設拒絕策略為AbortPolicy,即丟棄任務並丟擲RejectedExecutionException異常。我們可以通過程式碼來驗證這一點,現有如下程式碼:

public classThreadPoolTest{undefined
public static void main(String[] args) {undefined
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
ThreadFactory factory = r -> new Thread(r, "test-thread-pool");
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
0L, TimeUnit.SECONDS, queue, factory);
while (true) {undefined
executor.submit(() -> {undefined
try {undefined
System.out.println(queue.size());
Thread.sleep(10000);
} catch (InterruptedException e) {undefined
e.printStackTrace();
}
});
}
}
}

這裡是一個預設的執行緒池,沒有設定拒絕策略,設定了最大執行緒佇列是100。執行程式碼:


結果是符合預期的,這也證明了執行緒池的預設拒絕策略是ThreadPoolExecutor.AbortPolicy:丟棄任務並丟擲RejectedExecutionException異常。


四、設定執行緒池拒絕策略


如果我們想要根據實際業務場景需要,設定其他的執行緒池拒絕策略,可以通過ThreadPoolExecutor過載的構造方法進行設定:


現在的開發中,相信大家都有使用spring,其實我們也可以通過spring提供的org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor構建執行緒池。如下:


@Configuration
public classTaskExecutorConfigimplementsAsyncConfigurer{undefined
/**
* Set the ThreadPoolExecutor's core pool size.
*/

private static final int CORE_POOL_SIZE = 5;
/**
* Set the ThreadPoolExecutor's maximum pool size.
*/

private static final int MAX_POOL_SIZE = 5;
/**
* Set the capacity for the ThreadPoolExecutor's BlockingQueue.
*/

private static final int QUEUE_CAPACITY = 1000;
/**
* 通過重寫getAsyncExecutor方法,制定預設的任務執行由該方法產生
* <p>
* 配置類實現AsyncConfigurer介面並重寫getAsyncExcutor方法,並返回一個ThreadPoolTaskExevutor
* 這樣我們就獲得了一個基於執行緒池的TaskExecutor
*/

@Override
public Executor getAsyncExecutor() {undefined
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
taskExecutor.initialize();
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
return taskExecutor;
}
}


五、拒絕策略場景分析


(1)AbortPolicy
AbortPolicy

ThreadPoolExecutor.AbortPolicy:丟棄任務並丟擲RejectedExecutionException異常。

A handler for rejected tasks that throws a {@code RejectedExecutionException}.

這是執行緒池預設的拒絕策略,在任務不能再提交的時候,丟擲異常,及時反饋程式執行狀態。如果是比較關鍵的業務,推薦使用此拒絕策略,這樣子在系統不能承載更大的併發量的時候,能夠及時的通過異常發現。

(2)DiscardPolicy

ThreadPoolExecutor.DiscardPolicy:丟棄任務,但是不丟擲異常。如果執行緒佇列已滿,則後續提交的任務都會被丟棄,且是靜默丟棄。

A handler for rejected tasks that silently discards therejected task.

使用此策略,可能會使我們無法發現系統的異常狀態。建議是一些無關緊要的業務採用此策略。例如,本人的部落格網站統計閱讀量就是採用的這種拒絕策略。

(3)DiscardOldestPolicy

ThreadPoolExecutor.DiscardOldestPolicy:丟棄佇列最前面的任務,然後重新提交被拒絕的任務。

A handler for rejected tasks that discards the oldest unhandled request and then retries {@code execute}, unless the executor is shut down, in which case the task is discarded.

此拒絕策略,是一種喜新厭舊的拒絕策略。是否要採用此種拒絕策略,還得根據實際業務是否允許丟棄老任務來認真衡量。

(4)CallerRunsPolicy

ThreadPoolExecutor.CallerRunsPolicy:由呼叫執行緒處理該任務

  1. A handler for rejected tasks that runs the rejected task directly in the calling thread of the {@code execute} method, unless the executor has been shut down, in which case the task is discarded.

如果任務被拒絕了,則由呼叫執行緒(提交任務的執行緒)直接執行此任務,我們可以通過程式碼來驗證這一點:

把之前的程式碼修改如下:

  1. public static void main(String[] args) {
  2. BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);
  3. ThreadFactory factory = r -> new Thread(r, "test-thread-pool");
  4. ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
  5. 0L, TimeUnit.SECONDS, queue, factory, new ThreadPoolExecutor.CallerRunsPolicy());
  6. for (int i = 0; i < 1000; i++) {
  7. executor.submit(() -> {
  8. try {
  9. System.out.println(Thread.currentThread().getName() + ":執行任務");
  10. Thread.sleep(1000);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. });
  15. }
  16. }

把佇列最大值改為10,列印輸出執行緒的名稱。執行結果如下:

通過結果可以看到,主執行緒main也執行了任務,這正說明了此拒絕策略由呼叫執行緒(提交任務的執行緒)直接執行被丟棄的任務的。

六、總結


本文介紹和演示了四種執行緒池拒絕策略,具體使用哪種策略,還得根據實際業務場景才能做出抉擇。

原文連結:https://blog.csdn.net/suifeng629/article/details/98884972