Java 執行緒池四種拒絕策略

小碼code發表於2022-04-27

jdk1.5版本新增了 JUC 併發包,其中一個包含執行緒池。

四種拒絕策略:

拒絕策略型別 說明
1 ThreadPoolExecutor.AbortPolicy 預設拒絕策略,拒絕任務並丟擲任務
2 ThreadPoolExecutor.CallerRunsPolicy 使用呼叫執行緒直接執行任務
3 ThreadPoolExecutor.DiscardPolicy 直接拒絕任務,不丟擲錯誤
4 ThreadPoolExecutor.DiscardOldestPolicy 觸發拒絕策略,只要還有任務新增,一直會丟棄阻塞佇列的最老的任務,並將新的任務加入

預先配置

配置執行緒池。

  • 核心執行緒和最大執行緒都儘量設定的小一點,分別設定成 1 和 2
  • 阻塞佇列設定固定長度的有界佇列,長度為 1
  • 執行緒工廠設定預設執行緒工廠
// 核心執行緒數
int corePoolSize = 1;
// 最大執行緒數
int maximumPoolSize = 2;
// 執行緒存活時間
long keepAliveTime = 10;
// 執行緒存活時間單位
TimeUnit unit = TimeUnit.SECONDS;
// 有界佇列 遵循 FIFO 原則
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(1);
// 執行緒工廠
ThreadFactory threadFactory = Executors.defaultThreadFactory();

建立執行緒任務

建立執行緒任務,一個執行緒任務執行一秒:

class TaskThread implements Runnable{
                
		private int i;

		public TaskThread(int i) {
			this.i = i;
		}

		@Override
		public void run() {
			try {
				TimeUnit.SECONDS.sleep(2);
				System.out.println("執行任務:" + i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

拒絕策略一:AbortPolicy

預設拒絕策略,拒絕任務並丟擲任務

// 拒絕策略 預設拒絕策略,拒絕任務並丟擲異常:
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(corePoolSize,
				maximumPoolSize,
				keepAliveTime,
				unit,
				workQueue,
				threadFactory,
				handler);
		for (int i = 1; i <= 5; i++) {
			try {
				threadPool.execute(new TaskThread(i));
			} catch (Exception e) {
				System.out.println("【任務" + i + "】報錯:" + e.getMessage());
			}

		}

輸出

【任務】4報錯:Task com.test.controller.ThreadPoolController$TaskThread@5c0369c4 rejected from java.util.concurrent.ThreadPoolExecutor@50675690[Running, pool size = 2, active threads = 2, queued tasks = 1, completed tasks = 0]
【任務】5報錯:Task com.test.controller.ThreadPoolController$TaskThread@31b7dea0 rejected from java.util.concurrent.ThreadPoolExecutor@50675690[Running, pool size = 2, active threads = 2, queued tasks = 1, completed tasks = 0]
執行任務:1
執行任務:3
執行任務:2

最大執行緒數 + 阻塞佇列 = 3,執行到4,5的時候就丟擲錯誤。這裡需要用 try catch 捕獲異常。任務1、2、3正常執行。

如果提交的任務都要執行,可以將丟擲的錯誤任務存入在redis中,然後定時從redis中獲取任務,再提交執行。

拒絕策略二:CallerRunsPolicy

呼叫執行緒執行多餘的任務。

更換拒絕策略,將上面的 AbortPolicy 換成 CallerRunsPolicy

RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();

執行任務,輸出:

執行任務:1
執行任務:4
執行任務:3
執行任務:2
執行任務:5

最大執行緒數 + 阻塞佇列 = 3,多餘的任務還是繼續被執行。

拒絕策略三:DiscardPolicy

拒絕任務,不會丟擲錯誤。
更換策略,將CallerRunsPolicy 換成DiscardPolicy:

RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();

執行任務,輸出:

執行任務:1
執行任務:3
執行任務:2

多餘的執行緒任務提交被拒絕,而只執行最大執行緒數 + 阻塞佇列 數量的任務,並且不會丟擲錯誤。

拒絕策略四:DiscardOldestPolicy

只要還有任務新增,一直會丟棄阻塞佇列的最老的任務,並將新的任務加入到阻塞佇列中
更換策略,將DiscardPolicy 換成DiscardOldestPolicy:

RejectedExecutionHandler handler3 = new ThreadPoolExecutor.DiscardOldestPolicy();

執行任務,輸出:

執行任務:3
執行任務:1
執行任務:5

任務的執行順序是 核心執行緒數 —> 阻塞佇列 —> 最大執行緒數,其中任務1,任務3提交成功。

  • 任務2因為在阻塞佇列中,
  • 後面的任務4把任務2擠掉,
  • 任務5又把任務4擠掉,所以最後執行的是任務5。

總結

本文介紹了執行緒四種拒絕策略,當工作任務大於最大執行緒 + 阻塞佇列會執行阻塞佇列。

  • AbortPolicy 預設策略,拒絕任務,並丟擲異常
  • CallerRunsPolicy 呼叫執行緒執行對於的任務
  • DiscardPolicy 拒絕任務,不會丟擲異常
  • DiscardOldestPolicy 有多餘的任務,把阻塞佇列最老的任務丟棄,放入新的任務,直到沒有新的任務。
    如果覺得文章對你有幫助的話,請點個推薦吧!

相關文章