
1. 執行緒和硬體
1.1. 給CPU增加超執行緒并不能使應用程式性能翻倍
2. 執行緒池
2.1. 任務被提交到一個佇列(可能有不止一個佇列),然后一定數量的執行緒會從佇列中取出任務并執行它們
2.2. 執行緒池的大小對獲取最佳性能至關重要
- 2.2.1. 在某些情況下,過大的執行緒池會對性能造成損害
2.3. 執行緒池有最小執行緒數和最大執行緒數
-
2.3.1. 以最小數量的執行緒等待分配給它們的任務
-
2.3.2. 最大執行緒數可以起到必要的限流作用,防止執行緒同時執行過多的任務
2.4. CPU不是瓶頸,外部資源是,此時向執行緒池添加執行緒是有害的
-
2.4.1. 適用于向CPU密集型或I/O密集型的資料庫發送請求的REST服務器
-
2.4.2. 增加執行緒數實際上會降低總體吞吐量
2.5. 如果給瓶頸處增加負載,性能將大幅下降
2.6. 如果當前瓶頸的負載減少了,性能很可能會提升
2.7. 設定執行緒池的最大執行緒數往往是藝術性多于科學性
-
2.7.1. 高估執行緒池中需要的執行緒數可能也只造成了很小的性能損失
-
2.7.2. 一旦執行緒池的設定出現問題,應用程式就會出大問題
2.8. 設定最小執行緒數
-
2.8.1. 幾乎在所有的情況下,都可以將最小執行緒數設定成和最大執行緒數一樣的值
-
2.8.2. 設定為另一個值(例如設定成1)的理由是,它可以防止系統創建過多的執行緒,從而節省資源
-
2.8.3. 最好是創建最終可能需要的所有執行緒,同時要確保系統能夠處理預期的最大負載
-
2.8.4. 假設一個執行緒池的任務佇列預計平均有20個任務,那么對它來說20就是合適的最小執行緒數
2.9. 在批處理應用程式中,無論是在創建執行緒池時分配執行緒(將最小執行緒數和最大執行緒數設定為相同的值,就會出現這種情況),還是按需分配執行緒,都不重要,因為執行應用程式所需的時間是一樣的
2.10. 執行緒的空閑時間
3. ThreadPoolExecutor
3.1. 設定ThreadPoolExecutor的大小
3.2. 佇列型別
-
3.2.1. 并發佇列
-
3.2.1.1. 適用于管理少量任務,其他情況不適用
-
3.2.1.2. 你需要一個易于優化執行緒數的執行緒池,那么這個佇列型別是更好的選擇
-
3.2.1.3. 如果所有執行緒都在忙碌,而且池中的執行緒數小于最大執行緒數,那么新任務會啟動一個新執行緒
-
3.2.1.4. 這個佇列型別無法保留待處理的任務
-
3.2.1.5. 核心池大小是最小池大小,也就是空閑時也會保持運行的執行緒數
-
3.2.1.6. 最大池大小是池中的最大執行緒數
-
-
3.2.2. 無界佇列
-
3.2.2.1. 任何任務都不會被拒絕(因為佇列大小不受限制)
-
3.2.2.2. 執行器使用的執行緒數最多等于核心池大小,即最大池大小會被忽略
-
3.2.2.3. 由于佇列是無界的,因此如果任務的提交速度超過了運行速度,那么會有記憶體消耗過多的風險
-
-
3.2.3. 有界佇列
-
3.2.3.1. 采用了復雜的演算法來決定何時啟動新執行緒
-
3.2.3.2. 使得執行緒池可以作為一個限流器
-
3.2.3.3. 如果任務積壓得太多,執行緒池就會運行更多的執行緒來清理積壓的任務
3.2.3.3.1. 此時最大執行緒數可以作為第二個限流器
-
3.3. 不要使用Executors類來提供默認無界的執行緒池,這樣你無法控制應用程式的記憶體使用情況
3.4. 應該設定ThreadPoolExecutor,讓其有相同的核心執行緒數和最大執行緒數,并利用ArrayBlockingQueue來限制記憶體中待處理任務的數量
3.5. 父任務必須等待其子任務完成,而執行緒池執行器中的執行緒不能向佇列中添加另一個任務并等待任務完成,一旦其執行緒處于等待狀態,它就不能用來執行任何子任務了
3.6. 當任務很容易被分割成均衡的集合時,使用磁區的ThreadPoolExecutor會有更好的性能
4. ForkJoinPool
4.1. 當任務不均衡時,使用ForkJoinPool會有更好的性能
4.2. 允許它的執行緒創建新的任務,然后掛起當前任務,當任務被掛起時,其執行緒可以執行其他待處理任務
4.3. 為配合分治演算法設計的
-
4.3.1. 要確保拆分任務是有意義的
-
4.3.2. 應該用于遞回、分治演算法,它不適用于可以簡單分割處理的情況
4.4. 內部會使用一個無界任務串列,運行這些任務的執行緒數由其構造方法指定
4.5. 如果沒有向構造方法傳遞引數,那么執行緒池會基于機器上的(或Docker容器的)可用CPU數量來確定執行緒數
4.6. 實作了作業竊取
-
4.6.1. 意味著池中的每個執行緒都有一個自己所派生任務的佇列,執行緒會優先處理自己佇列中的任務,如果自己的佇列是空的,那么它們會從其他執行緒的佇列中竊取任務
-
4.6.2. ForkJoinPool中的其他執行緒也可以完成剩余的所有任務
- 4.6.2.1. ThreadPoolExecutor就不是這樣的了
4.7. 確定公共的ForkJoinTask池的大小,和確定任何其他執行緒池的大小一樣重要
-
4.7.1. 默認情況下,公共池的執行緒數和目標機器的CPU數一樣
-
4.7.2. 如果你在一臺機器上運行多個JVM,限制公共池的大小是有意義的,這樣JVM之間就不會相互爭奪CPU
4.8. -Djava.util.concurrent.ForkJoinPool.common.parallelism=N來設定公共池的大小
4.9. 如果你需要優化公共池的大小,可以考慮將所需的值減1
4.10. 創建太多任務會降低性能,但如果任務所需的時間不一樣,太少的任務也會降低性能
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/548107.html
標籤:其他
