JUC(4)---java線程池原理及源碼分析

線程池,既然是個池子裏面肯定就裝很多線程。

如果併發的請求數量非常多,但每個線程執行的時間很短,這樣就會頻繁的創建和銷燬 線程,如此一來會大大降低系統的效率。可能出現服務器在爲每個請求創建新線程和銷燬線 程上花費的時間和消耗的系統資源要比處理實際的用戶請求的時間和資源更多。因此Java中提供線程池對線程進行統一的管理使用。

線程池可以讓多個任務重用線程。減少線程創建,消亡的開銷,提高性能。當任務達到不需要等待線程創建便可立即執行。提高線程的可管理性。使用線程池進行統一的分配,調優和監控。

 

Executor框架體系:

Executor接口是線程池框架中最頂級的接口,定義了一個用於執行Runnable的execute方法

體系簡要類圖:

 

 ExecutorService也是一個重要的接口,其中也定義了一系列重要的方法:

  1. submit(task):可用來提交Callable或Runnable任務,並返回代表此任務的Future 對象
  2. shutdown():在完成已提交的任務後封閉辦事,不再接管新任務
  3. shutdownNow():停止所有正在履行的任務並封閉辦事。
  4. isTerminated():測試是否所有任務都履行完畢了。
  5. isShutdown():測試是否該ExecutorService已被關閉。.
  6. invokeXXX(task,...):執行給定任務

從類圖中可以看到一個很重要的實現類就是ThreadPoolExecutor,我們通過這個類看下線程池中比較重要的一些屬性。

 

 

 

ctl 是對線程池的運行狀態和線程池中有效線程的數量進行控制的一個字段, 它包含兩部分的信息: 線程池的運行狀態 (runState) 和線程池內有效線程的數量 (workerCount),這裏可以看到,使用了Integer類型來保存,高3位保存runState,低29位保存
workerCount。COUNT_BITS 就是29,CAPACITY就是1左移29位減1(29個1),這個常量表示workerCount的上限值,大約是5.3億。
線程池五種狀態:

RUNNING:ctl高三位111,可以接受新任務,並且處理已經添加的任務。線程池的初始化狀態就是running。

 

SHUTDOWN:ctl高三位000,不接受新任務,但是可以處理已經添加的任務。調用shutdown()方法,由running變成shutdown

 

STOP:ctl高三位001,不接受新任務,不處理已添加的任務,並且還會中斷正在處理的任務。調用shutdownNow()方法,由running/shutdown變成stop

 

TIDYING:ctl高三位010,當所有的任務已終止,ctl記錄的任務數量爲0,線程池會變爲tidying 狀態。當線程池變爲tidying 狀態時,會執行鉤子函數terminated()。terminated()在 ThreadPoolExecutor類中是空的,若用戶想在線程池變爲tidying 時,進行其他處理;可以通過重寫terminated()。線程池爲shutdown狀態,並且阻塞隊列是空的,並且執行的任務也是空就會由shutdown變成tidying;stop狀態時,線程池中任務爲空也會變成tidying

 

TERMINATED:ctl高三位011,線程池徹底涼涼了

當線程池處於tidying狀態並且執行完了terminated()方法,就會由tidying變成terminated

 

構造方法:

 

corePoolSize線程池中的核心線程數,當提交一個任務時,線程池創建一個新線程執行任務,直到當 前線程數等於corePoolSize;如果當前線程數爲corePoolSize,繼續提交的任務被保存到 阻塞隊列中,等待被執行;如果執行了線程池的prestartAllCoreThreads()方法,線程池會 提前創建並啓動所有核心線程。

maximumPoolSize線程池中允許的最大線程數。如果當前阻塞隊列滿了,且繼續提交任務,則創建新的線程執行任務,前提是當前線程數小於maximumPoolSize;

keepAliveTime線程池維護線程所允許的空閒時間。當線程池中的線程數量大於corePoolSize的時候,如果這時沒有新的任務提交,核心線程外的線程不會立即銷燬,而是會等待,直到等待的時間超過了keepAliveTime

unitkeepAliveTime的單位 

workQueue用來保存等待被執行的任務的阻塞隊列,且任務必須實現Runable接口,在JDK中提供瞭如下阻塞隊列:

  1、ArrayBlockingQueue:基於數組結構的有界阻塞隊列,按FIFO排序任務;

   2、LinkedBlockingQuene:基於鏈表結構的阻塞隊列,按FIFO排序任務,

  3、SynchronousQuene:一個不存儲元素的阻塞隊列,每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態

   4、priorityBlockingQuene:具有優先級的無界阻塞隊列;

threadFactory ThreadFactory,用來創建新線程。默認使用 Executors.defaultThreadFactory() 來創建線程。使用默認的ThreadFactory來創建線程時,會使新創建的線程具有相同的NORM_PRIORITY優先級並且是非守護線程,同時也設置了線程的名稱。

Handler線程池的拒絕策略,當阻塞隊列滿了,且沒有空閒的工作線程,如果繼續提交任務,必須採取一種策略處理該任務,線程池提供了4種策略:

  1、AbortPolicy:直接拋出異常,默認策略;

  2、CallerRunsPolicy:用調用者所在的線程來執行任務;

  3、DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務,並執行當前任務;

  4、DiscardPolicy:直接丟棄任務;

上面的4種策略都是ThreadPoolExecutor的內部類。 當然也可以根據應用場景實現RejectedExecutionHandler接口,自定義策略

 

通過execute或者submit向線程池提交任務。任務提交的時候先提交給核心線程(corePoolSize);如果核心線程滿了,就將任務放到workQueue裏面去排隊等待;如果隊列也滿了(取決用的什麼隊列,以及設置的大小),就會將新進來的任務提交給非核心線程,非核心線程數量等於maximumPoolSize - corePoolSize,非核心線程使用之後會被回收。如果非核心線程也滿了,那麼就執行相應的拒絕策略RejectedExecutionHandler

 

 

源碼解析:

1.execute方法:

 

 

 2. 可以看到關鍵方法是addWork方法,可以看到在addWork方法先會進行一系列判斷,如果都通過了,纔會進行任務的創建

 

 3.Worker內部類,這類是繼承了AbstractQueuedSynchronizer並且實現了Runable接口,其中還有兩個重要屬性一個是Thread, 一個是firstTask,初始化的時候會吧AQS中的state字段設置-1,後面允許中斷會將這個值修改。

會通過線程工廠創建一個線程和當前的worker綁定,創建線程的runable接口對象就是work本身。worker重寫的run方法實際調用了線程池的runWorker方法

 

 

 4. 回到addWorker方法,創建完了worker,可以就可以獲取到綁定的線程了,將worker添加到工作線程的集合中去。然後調用對象綁定線程的start方法,實際上會調用到worker的run方法,進而調用線程池的runWorker方法

 5. runWorker 方法中會去worker中取任務,如果firstTask空,就去隊列中取,因爲之前在addwork的時候有些場景傳入的firstWork是null。從隊列取不到任務了,也就是getTask返回null了,結束while循環,調用processWorkerExit方法移除任務,處理最終的一些狀態轉換

 

 

 

就是說通過線程調用worker的run方法,然後藉助worker裏面再來調用我們傳入線程池中的任務的run方法。所以說其實是起了一些線程,然後調用run方法一直嘗試去取任務,取到之後手動調用的run方法執行任務。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章