談談線程池的拒絕策略
是什麼
等待隊列也已經滿了,再也塞不下新任務了
同時,
線程池中的 max 線程也達到了,無法繼續爲新任務服務。
這時候我們就需要拒絕策略機制合理的處理這個問題。
JDK內置的拒絕策略
- AbortPolicy(默認):直接拋出 RejectedExecutionException 異常阻止系統正常運行。
- CallerRunsPolicy:“調用者運行”一種調節機制,該策略既不會拋棄任務,也不會拋出異常,而是將某些任務回退到調用者,從而降低新任務的流量。
- DiscardOldestPolicy:拋棄隊列中等待最久的任務,然後把當前任務加入隊列中嘗試再次提交當前任務。
- DiscardPolicy:直接丟棄任務,不予任何處理也不拋出異常。如果允許任務丟失,這是最好的一種方案。
以上內置拒絕策略均實現了 RejectedException
❌在工作中單一的 / 固定數的 / 可變的三種創建線程池的方式,用哪個多?超級大坑
答案是一個都不用,我們生產上只能使用自定義的,Executor 中 JDK 已經給我們提供了,爲什麼不用?
在工作中是如何使用線程池的,自定義線程池使用
package com.brian.interview.study.thread;
import java.util.concurrent.*;
/**
* Copyright (c) 2020 ZJU All Rights Reserved
* <p>
* Project: JavaSomeDemo
* Package: com.brian.interview.study.thread
* Version: 1.0
* <p>
* Created by Brian on 2020/2/13 18:01
*/
/**
* 第一種繼承 Thread 線程類
* 第二種實現 Runnable 接口(無返回值)
* 第三種實現 Callable 接口(有返回值)
*/
/**
* 第4種獲得/使用Java多線程的方式, 線程池
*/
public class MyThreadPoolDemo {
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());
// 自定義線程池
ExecutorService threadPool = new ThreadPoolExecutor(2, 5,
1L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
//new ThreadPoolExecutor.AbortPolicy() // 直接拋出 RejectedExecutionException 異常阻止系統正常運行。
//new ThreadPoolExecutor.CallerRunsPolicy() // “調用者運行”一種調節機制,該策略既不會拋棄任務,也不會拋出異常,而是將某些任務回退到調用者,從而降低新任務的流量。
//new ThreadPoolExecutor.DiscardOldestPolicy() // 拋棄隊列中等待最久的任務,然後把當前任務加入隊列中嘗試再次提交當前任務。
new ThreadPoolExecutor.DiscardPolicy() // 直接丟棄任務,不予任何處理也不拋出異常。如果允許任務丟失,這是最好的一種方案。
);
try {
// 模擬10個用戶來辦理業務, 每個用戶就是一個來自外部的請求線程
for (int i = 1; i <= 9; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t 辦理業務");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
private static void threadPoolInit() {
//ExecutorService threadPool = Executors.newFixedThreadPool(5); // 一池5個處理線程
//ExecutorService threadPool = Executors.newSingleThreadExecutor(); // 一池1個處理線程
ExecutorService threadPool = Executors.newCachedThreadPool(); // 一池N個處理線程
// 模擬10個用戶來辦理業務, 每個用戶就是一個來自外部的請求線程
try {
for (int i = 1; i <= 10; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t 辦理業務");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}
如何考慮合理配置線程池
CPU密集型
CPU密集的意思是該任務需要大量的運算,而沒有阻塞,CPU一直全速運行。
CPU密集任務只有在真正的多核CPU上纔可能得到加速(通過多線程),
而在單核CPU上(悲劇吧? (@_@😉),無論你開幾個模擬的多線程該任務都不可能得到加速,因爲CPU總的運算能力就那些。
CPU密集型任務配置儘可能少的線程數量:
一般公式:CPU核數+1個線程的線程池
IO 密集型
情況①
由於 IO 密集型任務線程並不是一直在執行任務,則應配置儘可能多的線程,如 CPU核數*2
情況②
IO 密集型,即該任務需要大量的IO,即大量的阻塞。
在單線程上運行IO密集型的任務會導致浪費大量的CPU運算能力浪費在等待。
所以在IO密集型任務中使用多線程可以大大的加速程序運行,即使在單核CPU上,這種加速主要就是利用了被浪費掉的阻塞時間。
IO密集型時,大部分線程都阻塞,故需要多配置線程數:
參考公式:CPU核數/(1-阻塞係數) 阻塞係數在0.8~0.9之間
比如8核CPU:8/(1-0.9)=80個線程數