1. 程式人生 > >執行緒池:Executor框架

執行緒池:Executor框架

無限制建立執行緒的不足

  1. 執行緒生命週期的開銷非常高。執行緒的建立並不是沒有代價的。根據平臺的不同,實際的開銷也有所不同,但是執行緒的建立過程都會需要時間,延遲處理的請求,並且需要JVM和作業系統提供一些輔導操作。如果請求的到達率非常高且請求的處理過程是輕量級的,例如大多數伺服器應用程式就是這種情況,那麼為每個請求建立一個新執行緒將消耗大量的計算資源。
  2. 資源消耗。活躍的執行緒會消耗資源,尤其是記憶體。如果可執行的執行緒數量多於可用處理器的數量,那麼有些執行緒將會閒置。大量的空閒的執行緒會佔用許多記憶體,給垃圾回收器帶來壓力,而且大量執行緒在競爭CPU資源時還將產生其他的效能開銷。如果你已經擁有足夠多的執行緒使所有cpu保持忙碌狀態,那麼再建立更多的執行緒反而會降低效能。
  3. 穩定性。在可建立執行緒的數量上存在一個限制。這個限制將隨著平臺的不同而不同,並且受多個因素制約,包括jvm的啟動引數、Thread建構函式中請求的棧大小,以及底層操作的限制等。如果破壞這些限制,那麼很可能丟擲OutOfMemoryError異常,要想從這種錯誤中恢復過來是非常危險的,更簡單的辦法是通過構造程式來避免超出這種限制。

以上摘抄自《JAVA併發程式設計實戰》

由以上問題看出,無限建立執行緒的危害,在jdk5以後提供Executor框架有效的為使用者提供了一個可用的方案

  • Executor基於生產者-消費者模式,提交任務相當於生產者(生成待完成的工作單元),執行任務的執行緒則相當於消費者(執行這些工作單元)。
  • 簡單使用:
    /**
     * 執行緒池測試
     */
    public class ThreadPoolExecutorTest {
    
        private static final Executor executor = Executors.newFixedThreadPool(10);
    
        /**
         * 執行緒池
         *
         * @param args
         */
        public static void main(String[] args) {
            for (int i = 0; i <= 100; i++) {
                int finalI = i;
                executor.execute(() -> System.out.println("執行執行緒" + finalI));
            }
        }
    }
    
    列印結果列印100次,執行順序也不是按照順序執行,說明是單獨執行緒每個執行的且執行100次
  • Executors工具類原始碼可以建立多種執行緒池,這裡主要和策略有關係(new的方式建立對應執行緒池也可以設定策略)原始碼比較多請參考:Executors建立的4種執行緒池的使用

執行緒池主要是通過狀態引數進行控制的,任務執行其實也是佇列的一種實現。有興趣的可以參考部落格看一看雨原始碼:JDK執行緒池原始碼分析之ThreadPoolExecutor,我覺得講的還是比較通俗易懂

到這裡我就想到Spring task定時任務了,所以專門百度了一下,然後順帶看了一點原始碼,大概囉嗦一下

  • Spring在任務排程時預設使用的還是ScheduledThreadPoolExecutor可以檢視部分原始碼ThreadPoolTaskScheduler類:

    
    	@UsesJava7
    	@Override
    	protected ExecutorService initializeExecutor(
    			ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
    
    		this.scheduledExecutor = createExecutor(this.poolSize, threadFactory, rejectedExecutionHandler);
    
    		if (this.removeOnCancelPolicy) {
    			if (setRemoveOnCancelPolicyAvailable && this.scheduledExecutor instanceof ScheduledThreadPoolExecutor) {
    				((ScheduledThreadPoolExecutor) this.scheduledExecutor).setRemoveOnCancelPolicy(true);
    			}
    			else {
    				logger.info("Could not apply remove-on-cancel policy - not a Java 7+ ScheduledThreadPoolExecutor");
    			}
    		}
    
    		return this.scheduledExecutor;
    	}
    
    
  • Spring在使用task排程時每個任務預設是單執行緒跑的,需要單獨設定執行緒池具體設定方式參考:Spring Boot 定時任務單執行緒和多執行緒spring task定時器的配置使用

最後吐槽一波,翻了好久,Spring task原始碼讀著還是很吃力的,希望自己能多提升一點能力吧。另外沒有詳細介紹執行緒池,是因為我覺得分享的部落格已經講得非常到位了,我也沒必要去copy,見諒吧。