標籤:executors odi 封裝 abort 無限 意思 最大值 儲存 大於
一.為什麼要用線程池1.減少了建立和銷毀線程的次數,每個背景工作執行緒都可以被重複利用,可執行多個任務。2.可以根據系統的承受能力,調整線程池中工作線線程的數目,防止因為消耗過多的記憶體,而把伺服器累趴下(每個線程需要大約1MB記憶體,線程開的越多,消耗的記憶體也就越大,最後死機)。Java裡麵線程池的頂級介面是Executor,但是嚴格意義上講Executor並不是一個線程池,而只是一個執行線程的工具。真正的線程池介面是ExecutorService。 二.比較幾個重要的類ExecutorService : 真正的線程池介面。ScheduledExecutorService : 能和Timer/TimerTask類似,解決那些需要任務重複執行的問題。ThreadPoolExecutor : ExecutorService的預設實現。ScheduledThreadPoolExecutor : 繼承ThreadPoolExecutor的ScheduledExecutorService介面實現,週期性任務調度的類實現。
1. 繼承關係: ThreadPoolExexutor繼承了AbstractExecutorService AbstractExecutorService實現了ExecutorService ExecutorService繼承了Executor
2. ThreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor幾個之間的關係: Executor是一個頂層介面,在它裡面只聲明了一個方法execute(Runnable),傳回值為void,參數為Runnable類型,從字面意思可以理解,就是用來執行傳進去的任務的; 然後ExecutorService介面繼承了Executor介面,並聲明了一些方法:submit、invokeAll、invokeAny以及shutDown等; 抽象類別AbstractExecutorService實現了ExecutorService介面,基本實現了ExecutorService中聲明的所有方法; 然後ThreadPoolExecutor繼承了類AbstractExecutorService。
3. ThreadPoolExecutor類中提供的四種構造方法:public class ThreadPoolExecutor extends AbstractExecutorService { public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);}
4. 各個參數的含義:
corePoolSize:核心池的大小,這個參數跟後面講述的線程池的實現原理有非常大的關係。在建立了線程池後,預設情況下,線程池中並沒有任何線程,而是等待有任務到來才建立線程去執行任務,除非調用了prestartAllCoreThreads()或者prestartCoreThread()方法, 從這2個方法的名字就可以看出,是預建立線程的意思,即在沒有任務到來之前就建立corePoolSize個線程或者一個線程。預設情況下,在建立了線程池後,線程池中的線程數為0,當有任務來之後,就會建立一個線程去執行任務,當線程池中的線程數目達到corePoolSize後,就會把到達的任務放到緩衝隊列當中;
maximumPoolSize:線程池最大線程數,這個參數也是一個非常重要的參數,它表示線上程池中最多能建立多少個線程
keepAliveTime:表示線程沒有任務執行時最多保持多久時間會終止。預設情況下,只有當線程池中的線程數大於corePoolSize時,keepAliveTime才會起作用,直到線程池中的線程數不大於corePoolSize,即當線程池中的線程數大於corePoolSize時, 如果一個線程閒置時間達到keepAliveTime,則會終止,直到線程池中的線程數不超過corePoolSize。但是如果調用了allowCoreThreadTimeOut(boolean)方法,線上程池中的線程數不大於corePoolSize時,keepAliveTime參數也會起作用,直到線程池中的線程數為0;
unit:參數keepAliveTime的時間單位,有7種取值,在TimeUnit類中有7種靜態屬性: TimeUnit.DAYS; //天 TimeUnit.HOURS; //小時 TimeUnit.MINUTES; //分鐘 TimeUnit.SECONDS; //秒 TimeUnit.MILLISECONDS; //毫秒 TimeUnit.MICROSECONDS; //微妙 TimeUnit.NANOSECONDS; //納秒
workQueue:一個阻塞隊列,用來儲存等待執行的任務,這個參數的選擇也很重要,會對線程池的運行過程產生重大影響,一般來說,這裡的阻塞隊列有以下幾種選擇 ArrayBlockingQueue:基於數組結構的有界阻塞隊列,按FIFO排序任務; LinkedBlockingQuene:基於鏈表結構的阻塞隊列,按FIFO排序任務,輸送量通常要高於 SynchronousQuene:一個不儲存元素的阻塞隊列,每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態,輸送量通常要高於ArrayBlockingQuene; PriorityBlockingQuene:具有優先順序的無界阻塞隊列;
threadFactory:線程工廠,主要用來建立線程;
handler:表示當拒絕處理任務時的策略,有以下四種取值: ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。 ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。 ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程) ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務 三.Java內建的四種線程池
newSingleThreadExecutor: 建立固定大小的線程池。每次提交一個任務就建立一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執行異常而結束,那麼線程池會補充一個新線程。
newFixedThreadPool: 建立固定大小的線程池。每次提交一個任務就建立一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執行異常而結束,那麼線程池會補充一個新線程。
newCachedThreadPool: 建立一個可快取的線程池。如果線程池的大小超過了處理任務所需要的線程,那麼就會回收部分空閑(60秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴於作業系統(或者說JVM)能夠建立的最大線程大小。
newScheduledThreadPool: 建立一個大小無限的線程池。此線程池支援定時以及周期性執行任務的需求 四.線程池的組成部分1.線程池管理器: 建立、銷毀並管理線程池,將背景工作執行緒放入線程池中2.背景工作執行緒: 一個可以迴圈執行任務的線程,在沒有任務時將進行等待3.任務列隊: 提供一種緩衝機制,將沒有處理的任務放在任務隊列中4.任務介面: 是每個任務必須實現的介面,主要用來規定任務的入口、任務執行完後的收尾工作、任務的執行狀態等,背景工作執行緒通過該介面調度任務的執行 五.PoolSize線程池中的核心線程數,當提交一個任務時,線程池建立一個新線程執行任務,直到當前線程數等於corePoolSize;如果當前線程數為corePoolSize,繼續提交的任務被儲存到阻塞隊列中,等待被執行;如果阻塞隊列滿了,那就建立新的線程執行當前任務;直到線程池中的線程數達到maxPoolSize,這時再有任務來,只能執行reject()處理該任務; 六.線程池的狀態(5種) 1、RUNNING:-1 << COUNT_BITS,即高3位為111,該狀態的線程池會接收新任務,並處理阻塞隊列中的任務; 2、SHUTDOWN: 0 << COUNT_BITS,即高3位為000,該狀態的線程池不會接收新任務,但會處理阻塞隊列中的任務; 3、STOP : 1 << COUNT_BITS,即高3位為001,該狀態的線程不會接收新任務,也不會處理阻塞隊列中的任務,而且會中斷正在啟動並執行任務; 4、TIDYING : 2 << COUNT_BITS,即高3位為010,該狀態表示線程池對線程進行整理最佳化; 5、TERMINATED: 3 << COUNT_BITS,即高3位為011,該狀態表示線程池停止工作; 七.向線程池提交任務
execute()內部實現1.首次通過workCountof()獲知當前線程池中的線程數, 如果小於corePoolSize, 就通過addWorker()建立線程並執行該任務; 否則,將該任務放入阻塞隊列;2. 如果能成功將任務放入阻塞隊列中, 如果當前線程池是非RUNNING狀態,則將該任務從阻塞隊列中移除,然後執行reject()處理該任務; 如果當前線程池處於RUNNING狀態,則需要再次檢查線程池(因為可能在上次檢查後,有線程資源被釋放),是否有閒置線程;如果有則執行該任務;3、如果不能將任務放入阻塞隊列中,說明阻塞隊列已滿;那麼將通過addWoker()嘗試建立一個新的線程去執行這個任務;如果addWoker()執行失敗,說明線程池中線程數達到maxPoolSize,則執行reject()處理任務;
sumbit()內部實現會將提交的Callable任務會被封裝成了一個FutureTask對象FutureTask類實現了Runnable介面,這樣就可以通過Executor.execute()提交FutureTask到線程池中等待被執行,最終執行的是FutureTask的run方法;比較:兩個方法都可以向線程池提交任務,execute()方法的傳回型別是void,它定義在Executor介面中, 而submit()方法可以返回持有計算結果的Future對象,它定義在ExecutorService介面中,它擴充了Executor介面,其它線程池類像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有這些方法。
總結executor() vs submit():(1)execute()方法實際上是Executor中聲明的方法,在ThreadPoolExecutor進行了具體的實現,這個方法是ThreadPoolExecutor的核心方法,通過這個方法可以向線程池提交一個任務,交由線程池去執行。(2)submit()方法是在ExecutorService中聲明的方法,在AbstractExecutorService就已經有了具體的實現,在ThreadPoolExecutor中並沒有對其進行重寫,這個方法也是用來向線程池提交任務的,但是它和execute()方法不同,它能夠返回任務執行的結果,去看submit()方法的實現,會發現它實際上還是調用的execute()方法,只不過它利用了Future來擷取任務執行結果。 八.線程池的關閉ThreadPoolExecutor提供了兩個方法,用於線程池的關閉,分別是shutdown()和shutdownNow(),其中:
shutdown():不會立即終止線程池,而是要等所有任務緩衝隊列中的任務都執行完後才終止,但再也不會接受新的任務
shutdownNow():立即終止線程池,並嘗試打斷正在執行的任務,並且清空任務緩衝隊列,返回尚未執行的任務 九.實現
import org.junit.Test;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class TestExecutor { @Test public void testSingleThreadExecutorPool() { ExecutorService pool = Executors.newSingleThreadExecutor(); Thread t1 = new MyThread(); Thread t2 = new MyThread(); Thread t3 = new MyThread(); Thread t4 = new MyThread(); Thread t5 = new MyThread(); pool.execute(t1); pool.execute(t2); pool.execute(t3); pool.execute(t4); pool.execute(t5); pool.shutdown(); /* pool-1-thread-1正在執行... pool-1-thread-1正在執行... pool-1-thread-1正在執行... pool-1-thread-1正在執行... pool-1-thread-1正在執行... */ } @Test public void testFixThreadExecutorPool() { //建立一個可重用固定線程數的線程池 ExecutorService pool = Executors.newFixedThreadPool(2); //建立實現了Runnable介面對象,Thread對象當然也實現了Runnable介面 Thread t1 = new MyThread(); Thread t2 = new MyThread(); Thread t3 = new MyThread(); Thread t4 = new MyThread(); Thread t5 = new MyThread(); //將線程放入池中進行執行 pool.execute(t1); pool.execute(t2); pool.execute(t3); pool.execute(t4); pool.execute(t5); //關閉線程池 pool.shutdown(); /* *pool-1-thread-1正在執行... *pool-1-thread-2正在執行... *pool-1-thread-1正在執行... *pool-1-thread-2正在執行... *pool-1-thread-1正在執行... */ } @Test public void testCachedThreadExecutorPool() { //建立一個可重用固定線程數的線程池 ExecutorService pool = Executors.newCachedThreadPool(); //建立實現了Runnable介面對象,Thread對象當然也實現了Runnable介面 Thread t1 = new MyThread(); Thread t2 = new MyThread(); Thread t3 = new MyThread(); Thread t4 = new MyThread(); Thread t5 = new MyThread(); //將線程放入池中進行執行 pool.execute(t1); pool.execute(t2); pool.execute(t3); pool.execute(t4); pool.execute(t5); //關閉線程池 pool.shutdown(); /* pool-1-thread-1正在執行... pool-1-thread-2正在執行... pool-1-thread-1正在執行... pool-1-thread-3正在執行... pool-1-thread-1正在執行... */ } @Test public void testScheduledThreadPoolExecutor() { ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1); exec.scheduleAtFixedRate(new Runnable() {//每隔一段時間就觸發異常 public void run() { System.out.println("================"); } }, 1000, 5000, TimeUnit.MILLISECONDS); exec.scheduleAtFixedRate(new Runnable() {//每隔一段時間列印系統時間,證明兩者是互不影響的 public void run() { System.out.println(System.nanoTime()); } }, 1000, 2000, TimeUnit.MILLISECONDS); } /** ================ 440697961331385 440699965178923 440701962726589 ================ 440703964380898 440705963957505 ================ 440707961439581 440709966042376 440711964335904 ================ */}
Java線程池