線程池ThreadPoolExecutor及原理

線程池的創建

     1.Executors直接創建:
        Executors.newCachedThreadPool();        創建無界的線程池,自動線程回收,
        Executors.newFixedThreadPool(n);          創建固定大小線程池
        Executors.new singleThreadExecutor();   創建單一後臺線程
        Executors.newScheduleThreadPool();      創建執行計劃的線程
     2.通過ThreadPoolExecutor創建: 下文介紹。
     3.建議:
           在阿里規範中:線程池不允許使用Executors來創建,而是通過ThreadPoolExecutor來創建, 這樣更能明確線
     程池的運行規則,規避資源耗盡的風險,
           Executors的各個弊端:
           1).newFixedThreadPool和newSingleThreadExecutor:
                  主要問題是堆積的請求處理隊列可能會耗費非常大的內存,甚至OOM(內存溢出)。
           2).newCachedThreadPool和newScheduledThreadPool:
                  主要問題是線程數最大數是Integer.MAX_VALUE,可能會創建數量非常多的線程,甚至OOM。

線程池的關閉:

      1.shutdown(): 調用該方法,不會馬上終止線程池,而是等所有緩存隊列中的任務都執行完才終止,但再也不會接受新的任務

      2.shutdownNow(): 立即終止線程池,並嘗試打斷正在執行的任務,並且清空任務緩存隊列,返回尚未執行的任務。

ThreadPoolExecutor:

      1.構造方法:
在這裏插入圖片描述
      2.構造方法參數:

corePoolSize int 核心線程池大小
maximumPoolSize int 最大線程池大小
keepAliveTime long 線程最大空閒時間
unit TimeUnit 線程最大空閒時間單位
workQueue BlockingQueue< Runnable > 線程阻塞隊列
threadFactory ThreadFactory 線程創建工廠
handler RejectedExecutionHandler 拒絕策略

      3.一般創建線程池方式:

在這裏插入圖片描述

      4.參數含義:
            1).corePoolSize: 表示改線程池中核心線程的大小。
                  線程池創建之後,線程池中的線程數爲0
                  當任務過來就創建一個核心線程去執行,直到線程數達到corePoolSize 之後,接受的任務放在阻塞隊列中。

核心線程:線程池中有兩類線程,核心線程和非核心線程。核心線程默認情況下會一直存在於線程池中,即使這個核心線程什麼都不幹(鐵飯碗),而非核心線程如果長時間的閒置,就會被銷燬(臨時工)

            2).maximumPoolSize: 最大線程池大小。
                線程池允許的最大線程數,該值等於核心線程數量 + 非核心線程數量。
                maximumPoolSize肯定是大於等於corePoolSize。
                通過設置corePoolSize和maximumPoolSize相同,可以創建一個FixedThreadPool;
            3).keepAliveTime: 線程最大空閒時間。
                如果線程池當前擁有超過corePoolSize的線程,多餘線程(非核心線程)超過keepAliveTime時就會被終止 。
                如果設置allowCoreThreadTimeOut(true),則會也作用於核心線程。
                默認情況下,只有線程池中線程數大於corePoolSize 時,keepAliveTime 纔會起作用。
            4).unit: keepAliveTime 的單位。
            5).workQueue: 線程阻塞隊列。
                當線程池中的線程數超過它的corePoolSize的時候,線程會進入阻塞隊列進行阻塞等待。
                 workQueue的類型爲BlockingQueue< Runnable >,通常可以取下面三種類型:
                a) ArrayBlockingQueue:
                    有界任務隊列,基於數組的先進先出隊列,此隊列創建時必須指定大小;
                b) LinkedBlockingQueue:
                    無界任務隊列,基於鏈表的先進先出隊列
                    如果創建時沒有指定此隊列大小,則默認爲Integer.MAX_VALUE;
                c) synchronousQueue:
                    直接提交隊列,這個隊列比較特殊,它不會保存提交的任務,而是將直接新建一個線程來執行新來的任務。
            6).threadFactory : 線程工廠,用來創建線程。
                Executors的線程池如果不指定線程工廠會使用Executors中的DefaultThreadFactory,
                默認線程池工廠創建的線程都是非守護線程。
            7).handler: 線程池的拒絕策略.
                當線程池的任務緩存隊列已滿並且線程池中的線程數目達到maximumPoolSize
                如果還有任務到來就會採取任務拒絕策略,通常有以下四種策略:
                a). AbortPolicy:
                    線程池默認會採用的是defaultHandler策略,即:
                    private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
                    該策略會丟棄任務 並拋出RejectedExecutionException異常。
                b). DiscardPolicy:
                    也是丟棄任務,但是不拋出異常。
                c).DiscardOldestPolicy:
                    線程池會丟棄阻塞隊列中末尾的任務 ,然後將被拒絕的任務添加到末尾。
                e).CallerRunsPolicy:
                    當有任務添加到線程池被拒絕時,線程池會將被拒絕的任務添加到”線程池正在運行的線程”中取運行。
                    線程池正在運行的線程:比如在main方法開啓線程池,執行拒絕策略時,main的線程就是正在運行的線程

線程池的任務處理策略:

      1.當前線程池中的線程數目小於corePoolSize:
          則每來一個任務,就會創建一個線程去執行這個任務;

      2.當前線程池中的線程數目>=corePoolSize
          則每來一個任務,會嘗試將其添加到任務緩存隊列當中
          若添加成功,則該任務會等待空閒的核心線程將其取出去執行;
          若添加失敗(一般來說是任務緩存隊列已滿),則會嘗試創建新的線程(非核心線程)去執行這個任務;
          如果當前線程池中的線程數目達到maximumPoolSize,則會採取任務拒絕策略進行處理;

線程池原理:

     1.線程池狀態:
          ThreadPoolExecutor類線程池的狀態 ,分別爲RUNNING、SHURDOWN、STOP、TIDYING、TERMINATED。
          線程池創建後處於RUNNING狀態。
          調用shutdown()方法後處於SHUTDOWN狀態
          調用shutdownNow()方法後處於STOP狀態
          當所有的任務已終止,線程池會變爲TIDYING狀態。接着會執行terminated()函數。
          線程池處在TIDYING狀態時,執行完terminated()方法之後, 線程池被設置爲TERMINATED狀態。

     2.線程池核心的execute()方法: 處理任務的核心方法是execute
在這裏插入圖片描述
          ctl.get()是獲取線程池狀態,用int類型表示。

     爲什麼要二次檢查線程池的狀態?
          第二步,入隊前進行了一次isRunning判斷,入隊後,又進行了一次isRunning判斷。
          倘若沒有二次檢查,萬一線程池處於非RUNNING狀態(在多線程環境下很有可能發生),那麼任務永遠不會執行

      3.線程複用:
          一個線程在創建的時候會指定一個線程任務,當執行完這個線程任務之後,線程自動銷燬。
          但是線程池卻可以複用線程,即一個線程執行完線程任務後不銷燬,繼續執行另外的線程任務。

          在addWorker方法中,會將線程任務firstTask封裝到工作線程worker裏
          然後再將工作線程add進工作線程組workers裏
在這裏插入圖片描述
在這裏插入圖片描述
          當t.start();啓動線程後,會觸發Worker類的run方法調用runWoker()方法。
在這裏插入圖片描述
          在while循環中,worker會不斷地調用getTask()方法從阻塞隊列中獲取任務。
          只要getTask方法不返回null,此線程就不會退出。
          然後調用task.run()執行任務,從而達到複用線程的目的。

推薦閱讀:
     http://concurrent.redspider.group/article/03/12.html

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