官术网_书友最值得收藏!

3.2 線程池的工作原理

Java線程池主要用于管理線程組及其運行狀態,以便Java虛擬機更好地利用CPU資源。Java線程池的工作原理為:JVM先根據用戶的參數創建一定數量的可運行的線程任務,并將其放入隊列中,在線程創建后啟動這些任務,如果線程數量超過了最大線程數量(用戶設置的線程池大?。?,則超出數量的線程排隊等候,在有任務執行完畢后,線程池調度器會發現有可用的線程,進而再次從隊列中取出任務并執行。

線程池的主要作用是線程復用、線程資源管理、控制操作系統的最大并發數,以保證系統高效(通過線程資源復用實現)且安全(通過控制最大線程并發數實現)地運行。

3.2.1 線程復用

在Java中,每個Thread類都有一個start方法。在程序調用start方法啟動線程時,Java虛擬機會調用該類的run方法。前面說過,在Thread類的run方法中其實調用了Runnable對象的run方法,因此可以繼承Thread類,在start方法中不斷循環調用傳遞進來的Runnable對象,程序就會不斷執行run方法中的代碼??梢詫⒃谘h方法中不斷獲取的Runnable對象存放在Queue中,當前線程在獲取下一個Runnable對象之前可以是阻塞的,這樣既能有效控制正在執行的線程個數,也能保證系統中正在等待執行的其他線程有序執行。這樣就簡單實現了一個線程池,達到了線程復用的效果。

3.2.2 線程池的核心組件和核心類

Java線程池主要由以下4個核心組件組成。

◎ 線程池管理器:用于創建并管理線程池。

◎ 工作線程:線程池中執行具體任務的線程。

◎ 任務接口:用于定義工作線程的調度和執行策略,只有線程實現了該接口,線程中的任務才能夠被線程池調度。

◎ 任務隊列:存放待處理的任務,新的任務將會不斷被加入隊列中,執行完成的任務將被從隊列中移除。

Java中的線程池是通過Executor框架實現的,在該框架中用到了Executor、Executors、ExecutorService、ThreadPoolExecutor、Callable、Future、FutureTask這幾個核心類,具體的繼承關系如圖3-2所示。

圖3-2

其中,ThreadPoolExecutor是構建線程的核心方法,該方法的定義如下:

public  ThreadPoolExecutor(int  corePoolSize, int  maximumPoolSize, long
    keepAliveTime, TimeUnit  unit, BlockingQueue<Runnable>  workQueue)  {
    this(corePoolSize,  maximumPoolSize,  keepAliveTime,  unit,  workQueue,
    Executors.defaultThreadFactory(),  defaultHandler);
}

ThreadPoolExecutor構造函數的具體參數如表3-1所示。

表3-1

3.2.3 Java線程池的工作流程

Java線程池的工作流程為:線程池剛被創建時,只是向系統申請一個用于執行線程隊列和管理線程池的線程資源。在調用execute()添加一個任務時,線程池會按照以下流程執行任務。

◎ 如果正在運行的線程數量少于corePoolSize(用戶定義的核心線程數),線程池就會立刻創建線程并執行該線程任務。

◎ 如果正在運行的線程數量大于等于corePoolSize,該任務就將被放入阻塞隊列中。

◎ 在阻塞隊列已滿且正在運行的線程數量少于maximumPoolSize時,線程池會創建非核心線程立刻執行該線程任務。

◎ 在阻塞隊列已滿且正在運行的線程數量大于等于maximumPoolSize時,線程池將拒絕執行該線程任務并拋出RejectExecutionException異常。

◎ 在線程任務執行完畢后,該任務將被從線程池隊列中移除,線程池將從隊列中取下一個線程任務繼續執行。

◎ 在線程處于空閑狀態的時間超過keepAliveTime時間時,正在運行的線程數量超過corePoolSize,該線程將會被認定為空閑線程并停止。因此在線程池中所有線程任務都執行完畢后,線程池會收縮到corePoolSize大小。

具體的流程如圖3-3所示。

圖3-3

3.2.4 線程池的拒絕策略

若線程池中的核心線程數被用完且阻塞隊列已排滿,則此時線程池的線程資源已耗盡,線程池將沒有足夠的線程資源執行新的任務。為了保證操作系統的安全,線程池將通過拒絕策略處理新添加的線程任務。JDK內置的拒絕策略有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy這4種,默認的拒絕策略在ThreadPoolExecutor中作為內部類提供。在默認的拒絕策略不能滿足應用的需求時,可以自定義拒絕策略。

1.AbortPolicy

AbortPolicy直接拋出異常,阻止線程正常運行,具體的JDK源碼如下:

public  static  class  AbortPolicy  implements  RejectedExecutionHandler  {
      public  AbortPolicy()  {  }
      public  void  rejectedExecution(Runnable  r,  ThreadPoolExecutor  e)  {
        //直接拋出異常信息,不做任何處理
          throw  new  RejectedExecutionException("Task  "  +  r.toString()  +
                                          "  rejected  from  "  +e.toString());
      }
    }

2.CallerRunsPolicy

CallerRunsPolicy的拒絕策略為:如果被丟棄的線程任務未關閉,則執行該線程任務。注意,CallerRunsPolicy拒絕策略不會真的丟棄任務。具體的JDK實現源碼如下:

public  void  rejectedExecution(Runnable  r,  ThreadPoolExecutor  e)  {
          if  (! e.isShutdown())  {
              r.run(); //執行被丟棄的任務r
          }
    }

3.DiscardOldestPolicy

DiscardOldestPolicy的拒絕策略為:移除線程隊列中最早的一個線程任務,并嘗試提交當前任務。具體的JDK實現源碼如下:

public  void  rejectedExecution(Runnable  r,  ThreadPoolExecutor  e)  {
          if  (! e.isShutdown())  {
            e.getQueue().poll(); //丟棄(移除)線程隊列中最老(最后)的一個線程任務
            e.execute(r); //嘗試提交當前任務
        }
}

4.DiscardPolicy

DiscardPolicy的拒絕策略為:丟棄當前的線程任務而不做任何處理。如果系統允許在資源不足的情況下丟棄部分任務,則這將是保障系統安全、穩定的一種很好的方案。具體的JDK實現源碼如下:

//直接丟棄線程,不做任何處理
public  void  rejectedExecution(Runnable  r,  ThreadPoolExecutor  e)  {
}

5.自定義拒絕策略

以上4種拒絕策略均實現了RejectedExecutionHandler接口,若無法滿足實際需要,則用戶可以自己擴展RejectedExecutionHandler接口來實現拒絕策略,并捕獲異常來實現自定義拒絕策略。下面實現一個自定義拒絕策略DiscardOldestNPolicy,該策略根據傳入的參數丟棄最老的N個線程,以便在出現異常時釋放更多的資源,保障后續線程任務整體、穩定地運行。具體的JDK實現源碼如下:

public  class  DiscardOldestNPolicy   implements  RejectedExecutionHandler  {
    private  int  discardNumber  =  5;
    private   List<Runnable>  discardList  =new  ArrayList<Runnable>();
    public  DiscardOldestNPolicy   (int  discardNumber)  {
      this.discardNumber  =  discardNumber;
    }
    public  void  rejectedExecution(Runnable  r,  ThreadPoolExecutor  e)  {
      if(e.getQueue().size()  >  discardNumber){
          //step 1:批量移除線程隊列中的discardNumber個線程任務
          e.getQueue().drainTo(discardList, discardNumber);
          discardList.clear(); //step 2:清空discardList列表
          if  (! e.isShutdown())  {
              e.execute(r); //step 3:嘗試提交當前任務
          }
      }
    }
}
主站蜘蛛池模板: 吉林省| 海宁市| 古浪县| 增城市| 彩票| 柘城县| 大港区| 航空| 凤山市| 于都县| 孟津县| 内乡县| 犍为县| 临高县| 隆昌县| 花垣县| 辉县市| 登封市| 三明市| 宁国市| 慈溪市| 新密市| 平果县| 民县| 乌兰察布市| 龙游县| 景东| 弥勒县| 尚志市| 太保市| 共和县| 东明县| 韩城市| 福建省| 新兴县| 化德县| 新兴县| 明水县| 佛山市| 昆明市| 高阳县|