Java線程池深入學習

線程池的實現原理

  • 接口:
    • Executor是一個頂層接口,在它裏面只聲明瞭一個方法execute(Runnable),返回值爲void,參數爲Runnable類型,從字面意思可以理解,就是用來執行傳進去的任務的;
    • 然後ExecutorService接口繼承了Executor接口,並聲明瞭一些方法:submit、invokeAll、invokeAny以及shutDown等;
    • 抽象類AbstractExecutorService實現了ExecutorService接口,基本實現了ExecutorService中聲明的所有方法;
    • 然後ThreadPoolExecutor繼承了類AbstractExecutorService。
  • 狀態
    • 在ThreadPoolExecutor中定義了一個volatile變量,另外定義了幾個static final變量表示線程池的各個狀態:volatile int runState;
      • static final int RUNNING    = 0;當創建線程池後,初始時,線程池處於RUNNING狀態;
      • static final int SHUTDOWN   = 1;如果調用了shutdown()方法,則線程池處於SHUTDOWN狀態,此時線程池不能夠接受新的任務,它會等待所有任務執行完畢;
      • static final int STOP       = 2;如果調用了shutdownNow()方法,則線程池處於STOP狀態,此時線程池不能接受新的任務,並且會去嘗試終止正在執行的任務;
      • static final int TERMINATED = 3;當線程池處於SHUTDOWN或STOP狀態,並且所有工作線程已經銷燬,任務緩存隊列已經清空或執行結束後,線程池被設置爲TERMINATED狀態。
  • 線程創建的三種方法:
    • 1、繼承Thread類創建線程
    • 2、實現Runnable接口創建線程
    • 3、實現Callable接口通過FutureTask包裝器來創建Thread線程
    • 4、使用ExecutorService、Callable、Future實現有返回結果的線程
    • future接口:
      • get獲取計算結果
      • 可取消任務(任務完成後無法取消)
      • FutureTask實現類:適配器模式
        • FutureTask(Callable<v>,callable)運行指定的callable
        • FutureTask(Runnable runnable ,v result)運行指定的Runnable,並在成功完成時get返回給定結果。其中包含定義狀態變量state,new(0)--->completing(1) ------>normal(2)正常狀態,exp(3)異常,cancell(4)取消,interrupting(5)打斷中,interrupted(已經被打斷),outcome接受返回值
          • t.start()實際上是調用FutureTask中的run方法, 調用重寫的call方法,-----》set方法將狀態從0變成1,再將返回值賦值給outcome,然後狀態改變爲normal2
          • 再調用get方法是時,會判斷狀態,當 狀態等於2時,將outcome返回給get
      • Callable是一種可以返回結果的任務,通過適配器模式讓runnable和callable像類似,Future代表一個異步計算,可以get到計算結果,查看計算狀態。其實現FutureTask可以以被提交給Executor執行,多個線程可以得到計算結果。Callable和Future是配合使用的,當Future get到計算結果的時,如果還沒被計算出來,那麼線程將被掛起,內部使用一個單鏈表位置等待過程,當計算出結果後,等待線程解除掛起,等待線程就可以得到計算結果了

  • 四種線程池?區別?線程池參數?
    • Executor框架
      • new Thread()缺點
        • 每次new耗費性能
        • new出來的線程可以無限創建,直接會互相競爭,過多佔用系統資源
        • 不利於擴展(定時執行,定期執行,線程中斷)
      • 採用線程池:重複使用存在的線程,減少對象的創建、消亡帶來的開銷,有效控制最大併發數,提高系統資源的使用率。同時避免過多資源競爭、避免堵塞、提供定時執行、定期執行、單線程和多線程併發控制等功能
      • Executor:異步執行框架,將任務提交和運行過程解耦,基於生產者-消費者模式,用runnable表示任務,Executor還提供了對生命週期的支持,以及統計信息收集,應用程序管理機制和性能監視等機制
        • Executor接口定義了最基本的Execute方法,用於接受用戶提交任務
        • ExecutorService定義了線程池終止shoutdown和submit提交futureTask任務支持方法。submit提交runnable和callable,返回future可引用實例;當excute提交任務後,任務進入BlockingDeque隊列,然後由submit執行任務並返回結果
          • isShutdown boolean isShutdown() 如果這個執行者已被關閉,則返回 true 。
          • shutdown()啓動有序關閉,其中先前提交的任務將被執行,但不會接受任何新任務。執行完一個關閉一個,順序關閉
          • isTerminated()如果所有任務在關閉後完成,則返回 true 
          • <T> Future<T> submit(Callable<T> task)提交值返回任務以執行,並返回代表任務待處理結果的Future。
          • Future<?> submit(Runnable task)提交一個可運行的任務執行,並返回一個表示該任務的未來。
          • <T> Future<T> submit(Runnable task, T result)提交一個可運行的任務執行,並返回一個表示該任務的未來。
        • ThreadPoolExecutor

          • 線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。
            • 說明:Executors 返回的線程池對象的弊端如下:
              • 1)FixedThreadPool 和 SingleThreadPool:允許的請求隊列長度爲 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。
              • 2)CachedThreadPool 和 ScheduledThreadPool:允許的創建線程數量爲 Integer.MAX_VALUE,可能會創建大量的線程,從而導致 OOM。
          • 一個ExecutorService ,使用可能的幾個合併的線程執行每個提交的任務,通常使用Executors工廠方法配置。
            • Executors.newCachedThreadPool() (無限線程池,具有自動線程回收) 底層的實現類還是ThreadPoolExecutor ,阻塞隊列無限127 SynchronousQueue
            •  Executors.newFixedThreadPool(int) (固定大小的線程池)對於ThreadPoolExecutor ,核心線程數是3,最大線程數也是3 LinkedBlockingQueue
            • Executors.newSingleThreadExecutor() (單個後臺線程)LinkedBlockingQueue
            • Executors.newSingleThreadScheduledExecuto(創建一個單線程執行器,可以調度命令在給定的延遲之後運行,或定期執行。 )
          • 如果當前線程池中的線程數目小於corePoolSize,則每來一個任務,就會創建一個線程去執行這個任務;
          • 如果當前線程池中的線程數目>=corePoolSize,則每來一個任務,會嘗試將其添加到任務緩存隊列當中,若添加成功,則該任務會等待空閒線程將其取出去執行;若添加失敗(一般來說是任務緩存隊列已滿),則會嘗試創建新的線程去執行這個任務;
          • 如果當前線程池中的線程數目達到maximumPoolSize,則會採取任務拒絕策略進行處理;
          • 如果線程池中的線程數量大於 corePoolSize時,如果某線程空閒時間超過keepAliveTime,線程將被終止,直至線程池中的線程數目不大於corePoolSize;如果允許爲核心池中的線程設置存活時間,那麼核心池中的線程空閒時間超過keepAliveTime,線程也會被終止。
  • 線程池的狀態

    • 線程池的5種狀態:Running、ShutDown、Stop、Tidying、Terminated。

    • 1、RUNNING
      • (1) 狀態說明:線程池處在RUNNING狀態時,能夠接收新任務,以及對已添加的任務進行處理。
      • (2) 狀態切換:線程池的初始化狀態是RUNNING。換句話說,線程池被一旦被創建,就處於RUNNING狀態,並且線程池中的任務數爲0!
    • 2、 SHUTDOWN
      • (1) 狀態說明:線程池處在SHUTDOWN狀態時,不接收新任務,但能處理已添加的任務。
      • (2) 狀態切換:調用線程池的shutdown()接口時,線程池由RUNNING -> SHUTDOWN。
    • 3、STOP
      • (1) 狀態說明:線程池處在STOP狀態時,不接收新任務,不處理已添加的任務,並且會中斷正在處理的任務。
      • (2) 狀態切換:調用線程池的shutdownNow()接口時,線程池由(RUNNING or SHUTDOWN ) -> STOP。
    • 4、TIDYING
      • (1) 狀態說明:當所有的任務已終止,ctl記錄的”任務數量”爲0,線程池會變爲TIDYING狀態。當線程池變爲TIDYING狀態時,會執行鉤子函數terminated()。terminated()在ThreadPoolExecutor類中是空的,若用戶想在線程池變爲TIDYING時,進行相應的處理;可以通過重載terminated()函數來實現。
      • (2) 狀態切換:當線程池在SHUTDOWN狀態下,阻塞隊列爲空並且線程池中執行的任務也爲空時,就會由 SHUTDOWN -> TIDYING。當線程池在STOP狀態下,線程池中執行的任務爲空時,就會由STOP -> TIDYING。
    • 5、 TERMINATED
      • (1) 狀態說明:線程池徹底終止,就變成TERMINATED狀態。
      • (2) 狀態切換:線程池處在TIDYING狀態時,執行完terminated()之後,就會由 TIDYING -> TERMINATED。
  • workQueue的類型爲BlockingQueue<Runnable>,通常可以取下面三種類型:
    • 1)ArrayBlockingQueue:基於數組的先進先出隊列,此隊列創建時必須指定大小;
    • 2)LinkedBlockingQueue:基於鏈表的先進先出隊列,如果創建時沒有指定此隊列大小,則默認爲Integer.MAX_VALUE;
    • 3)synchronousQueue:這個隊列比較特殊,它不會保存提交的任務,而是將直接新建一個線程來執行新來的任務。
  • 線程池的任務緩存隊列已滿並且線程池中的線程數目達到maximumPoolSize,如果還有任務到來就會採取任務拒絕策略,通常有以下四種策略:
    • ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。
    • ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。
    • ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
    • ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
  • 線程池容量設計
    • 如果是CPU密集型任務,就需要儘量壓榨CPU,參考值可以設爲 CPU+1
    • 如果是IO密集型任務,參考值可以設置爲2*CPU
    • 當然,這只是一個參考值,具體的設置還需要根據實際情況進行調整,比如可以先將線程池大小設置爲參考值,再觀察任務運行情況和系統負載、資源利用率來進行適當調整。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章