線程池初步入門綜合講解

目錄

  1. 線程池優點
  2. 線程池七大參數介紹
  3. Executors提供的api創建線程池
  4. 手動創建線程池,new ThreadPoolExecutor
  5. 線程池底層工作原理

正文

  1. 線程池優點
    線程是稀缺資源,使用線程池可以減少創建和銷燬線程的次數,減小系統的開銷
    每個工作線程都可以重複使用,提高資源使用率
    可以根據系統的承受能力,調整線程池中工作線程的數量,防止因爲消耗過多內存導致服務器崩潰

  2. 線程池的七大參數介紹
    看源代碼都可以知道,創建線程池最終都是要使用到下面的那個構造方法

    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler) {
            if (corePoolSize < 0 ||
                maximumPoolSize <= 0 ||
                maximumPoolSize < corePoolSize ||
                keepAliveTime < 0)
                throw new IllegalArgumentException();
            if (workQueue == null || threadFactory == null || handler == null)
                throw new NullPointerException();
            this.corePoolSize = corePoolSize;
            this.maximumPoolSize = maximumPoolSize;
            this.workQueue = workQueue;
            this.keepAliveTime = unit.toNanos(keepAliveTime);
            this.threadFactory = threadFactory;
            this.handler = handler;
        }
    

    一共七個參數

      /**
        * 參數解釋:
        * corePoolSize:核心線程數,包括空閒線程數
        * maximumPoolSize:最大線程數,允許的最多線程數(當請求大於核心線程數以及等待隊列的最大等待數時,就繼續創建線程,但不能超過這裏規定的最大線程)
        * keepAliveTime:當 當前線程數 > 核心線程數 ,這些多出來的線程在沒有被使用(任務調用)的情況下的存活最長時間
        * unit:時間單位
        * BlockingQueue<Runnable>:等待隊列(當請求超過核心線程數的時候,多餘的請求進入這裏的等待隊列)
        * ThreadFactory:線程創建工廠,生成線程的方式,一般默認 DefaultThreadFactory
        * RejectedExecutionHandler:拒絕策略,當請求越來越多,超過了最大線程數以及等待隊列的最大存放數,那這個時候就需要拒絕接下來的請求了,提供的拒絕策略    
        */
    

    Java提供了四種拒絕策略

    a:AbortPolicy:直接拋出異常

    b:CallerRunsPolicy:返回給調用者所在的線程運行該任務

    c:DiscardOldestPolicy:丟棄隊列裏最近的一個任務,並執行當前任務

    d:DiscardPolicy:不處理,丟棄掉

  3. Executors提供的api創建線程池
    Executors跟Executor的關係就像Collections和Collection的關係
    Executor是線程池的頂級父接口,其下有很多實現子類
    Executors是爲了操作線程池提供的一些封裝好的Api

    常見創建線程池方法:
    newFixedThreadPool

    // 定長線程池,線程池中線程數固定,核心線程數等於最大線程數
    ExecutorService threadPool = Executors.newFixedThreadPool(6);
    
    // 源代碼,對照着參數可以很簡單的理解
    	public static ExecutorService newFixedThreadPool(int nThreads) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>());
        }
    

    newSingleThreadExecutor

    // 單線程線程池,池中就只有一個線程,最大線程數也是1
    ExecutorService threadPool = Executors.newSingleThreadExecutor();
    
    // 源碼
    public static ExecutorService newSingleThreadExecutor() {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>()));
        }
    

    newCachedThreadPool

    // 可緩衝線程池,核心線程數爲0,但可以動態擴展到越來越大,同時也有失活時間
    ExecutorService threadPool = Executors.newCachedThreadPool();
    
    // 源碼
    public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>());
        }
    
  4. 手動創建線程池,new ThreadPoolExecutor
    首先看一下阿里巴巴Java開發手冊,來分析一下爲什麼阿里官方不推薦我們使用Executors提供的api創建線程池
    在這裏插入圖片描述
    理解起來還是很簡單的,上面也看了相關源碼以及七大參數的意思

    1)使用newFixedThreadPoolnewSingleThreadExecutor申明的等待隊列

    new LinkedBlockingQueue()無參構造方法,那默認最大容量就是Integer.MAX_VALUE,就是一個無界隊列

    這樣一來當有大量請求來的時候,他們都會在堆積在等待隊列中(不會生效拒絕策略),從而引發內存溢出等異常

    2)使用newCachedThreadPoolScheduledThreadPool定義的最大線程數都是Integer.MAX_VALUE

    同樣的道理,當有大量請求來的時候,會不斷地創建線程數(不會生效拒絕策略),從而引發內存溢出等異常

    3)推薦我們使用new ThreadPoolExecutor方式自己手動創建線程池,這樣可以根據自己服務器的配置合理的設置線程池參數,不至於像上面那樣僅適用於學習理論教學階段的使用,然後實際開發生產過程中將會遇到一系列問題

    // 示例
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 10, 
    				30L, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<>(5), 
                    Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
    
  5. 線程池工作原理
    在這裏插入圖片描述
    簡單的畫了一下,工作原理(工作流程)

    還有一點需要注意的是,當 當前線程數 > 核心線程數 ,這些多出來的線程在沒有被使用(任務調用)的情況下的有一個存活最長時間,利用這種機制可以及時回收那些多創建的線程(大於線程數)

  6. 最後總結

    這只是我學習線程池的入門第一步,大致瞭解了什麼是線程池、線程池的參數意義、創建方式、工作原理等一些注意事項
    真正還需要深入的瞭解,那就要多看看底層的源代碼了
    一起加油哈

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