執行緒池在創建的時候啟動一定數量的執行緒,這些執行緒所做的事情就是不斷從任務佇列中獲取任務來執行,當已啟動的執行緒全部并行執行任務,即所有已有執行緒都處于繁忙狀態,且任務佇列滿了的時候,管理執行緒可以再啟動一些新的執行緒來執行任務,已啟動的執行緒數不能超過執行緒池設定的總大小,當繁忙態執行緒低于一定比例時,可以考慮釋放掉一部分執行緒,但最少會有多少的執行緒會一直存在,這個多少的值可以在啟動執行緒池時設定,所以有了執行緒池就不用一直啟動再釋放執行緒,同時可以讓少于任務數個的執行緒來服務比較多的任務,比如執行緒池大小為16,任務數為50,這時執行緒池會先取16個任務執行,此時再來新的任務會被存到任務佇列中,相當于一個快取池的作用,可以看出用16個執行緒就可以服務50多個任務了,
這里面的任務佇列會被執行緒池里面的執行緒讀取,看是否有任務存在,所以任務佇列對于執行緒池來說是一種臨界資源,訪問的時候就要加鎖了,
執行緒是怎么判斷任務佇列是否有任務到來呢?如果用while回圈判斷任務佇列的大小,就會造成cpu的浪費,所以這里一般用條件變數來實作,條件變數要結合互斥鎖使用,wait之前上鎖,wait時自動解鎖,被喚醒后如果條件滿足會繼續持有鎖,這里wait用來等待信號,比如有新的任務到來時可以發起一個信號,wait時會進入阻塞狀態,不會消耗cpu,被喚醒后再檢查條件,如果確實有任務了,執行緒就可以取任務進行執行了,這里取任務是在上面的互斥鎖保護下進行的,所以不會有多取的情況,
這里執行緒池的執行緒從任務佇列里面消費任務,而用戶則可以向任務佇列里面生產任務,這就是一個生產者消費者模型,java里面一般將這個任務佇列設計成阻塞佇列,沒有任務但是執行緒去取的時候就會阻塞,如果阻塞佇列還是有大小限制的,如是用陣列實作的,則佇列滿了的時候用戶就阻塞了,放不進去任務,當然可以設計成別的策略,如直接拋棄想要加進去的任務,這樣執行緒池和阻塞任務佇列隔離實作了,不再耦合在一起,也就是將條件變數的wait和signal放在任務佇列里面去實作,
一個專案里面可以使用多個執行緒池,一個執行緒池對應一個任務佇列,比如io密集型的任務放在同一個執行緒池里,cpu密集型的任務放在另外一個執行緒池里,這樣即使io密集型的任務全部阻塞,也還可以有cpu密集型的任務得到運行,起到隔離的作用,
當任務的執行時間比較短,執行緒池對應的任務佇列里面的條件變數互斥鎖就可能成為性能瓶頸,因為會有多執行緒高并發讀寫任務佇列,這個時候可以考慮用cas進行無鎖優化,另外任務佇列也可以進一步優化,加入任務優先級的功能,優先級高的任務優先得到取出,優先讓執行緒池里面的執行緒處理,
本文作者: nephen
本文鏈接: https://www.nephen.cn/posts/a0be7773/
著作權宣告: 本博客所有文章除特別宣告外,均采用 CC BY-NC-SA 3.0 許可協議,轉載請注明出處!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/458369.html
標籤:其他
上一篇:golang初探
