線程池在生產上如何設置合理參數

談談線程池的拒絕策略

是什麼

等待隊列也已經滿了,再也塞不下新任務了
同時,
線程池中的 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個線程數

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