1. 程式人生 > >執行緒池使用總結

執行緒池使用總結

執行緒池使用總結

Excutors工廠類

為了更好的控制更多的多執行緒,JDK提供返回一個固定數量的執行緒池。

方法:

  • newFixedThreadPool()方法:
    改方法返回一個固定數量的執行緒池,改方法的執行緒數,始終不變,當一個任務提交時,若執行緒池空閒,則立即執行,若沒有,則會被暫緩在一個任務佇列中等待有空閒的執行緒去執行。
  • newSingleThreadPool()方法
    建立一個單例的執行緒池,若空閒則執行,若沒有空閒執行緒則暫緩在任務佇列中
  • newCachedThreadPool()方法
    返回一個可根據實際情況調整執行緒個數的執行緒池,不限制最大執行緒數量,若有任務,則建立執行緒,若無任務則不建立執行緒,如果沒有任務則執行緒會在60s後自動回收。
  • newScheduledThreadPool()方法:該方法返回一個ScheduledExcutorService物件,但該執行緒池可以指定執行緒的數量。

自定義執行緒池-ThreadPoolExecutors

  • 自定義執行緒池:ThreadPoolExecutor

執行緒池相關引數的配置

 ThreadPoolExecutor(int corePoolSize, //核心執行緒數,執行緒初始化就會被建立
                              int maximumPoolSize, //執行緒池最大執行緒數,無界佇列時,不起作用
                              long keepAliveTime, //執行緒的存活時間
                              TimeUnit unit,      //時間的單位
                              BlockingQueue<Runnable> workQueue, //阻塞佇列,使用無界佇列時,拒絕策略無用
                              ThreadFactory threadFactory,//theadFactory 執行緒工廠,用於獲取一個新的執行緒,然後把該執行緒投遞到執行緒池裡面去
                              RejectedExecutionHandler handler) //拒絕策略

1.當設定為有界佇列時:

a 若有新的任務需要執行,如果執行緒實際數小於coreThread,則先建立執行緒

b.若大於coreSize,則會加入任務佇列

c.若佇列已滿,則在不大於maxinumPoolsize的情況下建立新的執行緒

d.若執行緒數不大於maxinumPoolSize會執行拒絕策略

2.當為無界佇列時

a.maximumPoolSize不起作用,也沒有拒絕策略

b.若有新的任務需要執行,如果執行緒實際數小於coreThread,則先建立執行緒

c.若大於coreSize,則會加入任務佇列,同時建立新的執行緒。

如何使用好執行緒池?

  • 執行緒個數大小的設定
  • 執行緒池相關引數的配置
  • 利用Hook嵌入你的行為
  • 執行緒池的關閉

執行緒池數量的設定

計算機密集型

應用需要非常多的CPU計算資源,避免過多的執行緒上下文切換

執行緒數 = CPU核數+1,已可以設定為CPU核數*2,還要看JDK的版本以及CPU配置(伺服器的CPU有超執行緒)

IO密集型

Web應用,涉及到大量的網路傳輸,不僅如此,與資料庫,與快取間的互動也涉及到IO,一旦發生IO,執行緒就會處於等待狀態,當IO結束後,資料準備好後,執行緒才會繼續執行。對於IO密集型的應用,我們可以多設定執行緒池中執行緒的數量,這樣就能讓等待IO的這段時間內,執行緒可以去做其它事,提高併發處理效率。執行緒上下文切換數有代價的

執行緒數 = CPU核數/(1-阻塞係數) 這個阻塞係數一般為0.8~0.9之間。

套用公司:對於雙核CPU來說,比較理想的執行緒數就是20,當然這不是絕對的,需要根據實際情況以及實際業務來調整:final int poolSIze = (int)(copCore/(1-0.9))

執行緒池相關引數如何配置?

  • 高壓線:
    • 我們使用執行緒池的時候都不要選擇沒有上限限制的配置項
  • 第一 我們不要去使用沒有上限的執行緒池和設定無界佇列!
  • 比如,newCachedThreadPool的設定與無界佇列因為某些不可預期的情況,執行緒池會出現系統異常,導致執行緒暴增的情況或者任務佇列或者任務佇列不斷膨脹,記憶體耗盡導致系統崩潰。我們建議自定義執行緒池來避免改問題,這是在使用執行緒池的首要選擇。小心無大錯,千萬別過度自信。
  • 第二 合理設定執行緒數量、和執行緒空閒回收時間,根據具體的任務執行週期和時間去設定,避免頻繁的回收和建立,雖然我們使用執行緒池的目的是為了提升系統性能和吞吐量,但是也要考慮下系統的穩定性,不然出現不可預期問題會很麻!
  • 第三,根據實際場景,選擇適用於自己的拒絕策略。進行補償,不要亂用JDK支援的自動補償機制~儘量採用自定義的拒絕策略去進行兜底!

利用Hook去嵌入你的行為

  • 利用Hook,留下執行緒池執行的執行軌跡
  • ThreadPoolExcutor提供了protected型別可以被覆蓋的鉤子方法,執行使用者在執行任務之前或執行之後做一些事情。我們可以通過它來實現比如初始化ThreadLocal、收集統計資訊、如記錄日誌等操作,這類Hook如BeforeExcute和afterExecute。另外還有一個Hook可以用來在任務唄執行完的時候讓使用者插入邏輯,如reminated。
  • Hook方法執行失敗,則內部的工作執行緒的執行將會失敗或者中斷。

關閉執行緒池

  • 內容當執行緒池不在被引用並且工作執行緒數為0的時候,執行緒池將被終止。我們也可以呼叫shutdown來手動終止執行緒池。如果我們忘記呼叫shutdown,為了讓執行緒資源被釋放,我們還可以使用keepAliveTime和allowShutdownHook方法,手工去呼叫執行緒池的關閉方法!
  • 最穩妥的方式是使用虛擬機器Runtime.getRuntime().addShutdownHook方法,手動去呼叫執行緒池關閉方法。