問區別,以表格形式做區別; 問流程,以流程圖解釋; 問概念,解釋+通俗易懂日常常見事物/編程常見; 不說廢話,精煉 持更 ...
線程池在創建的時候啟動一定數量的線程,這些線程所做的事情就是不斷從任務隊列中獲取任務來執行,當已啟動的線程全部並行執行任務,即所有已有線程都處於繁忙狀態,且任務隊列滿了的時候,管理線程可以再啟動一些新的線程來執行任務,已啟動的線程數不能超過線程池設置的總大小,當繁忙態線程低於一定比例時,可以考慮釋放掉一部分線程,但最少會有多少的線程會一直存在,這個多少的值可以在啟動線程池時設置。所以有了線程池就不用一直啟動再釋放線程,同時可以讓少於任務數個的線程來服務比較多的任務,比如線程池大小為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 許可協議。轉載請註明出處!