線程池的實現原理
- 接口:
- Executor是一個頂層接口,在它裏面只聲明瞭一個方法execute(Runnable),返回值爲void,參數爲Runnable類型,從字面意思可以理解,就是用來執行傳進去的任務的;
- 然後ExecutorService接口繼承了Executor接口,並聲明瞭一些方法:submit、invokeAll、invokeAny以及shutDown等;
- 抽象類AbstractExecutorService實現了ExecutorService接口,基本實現了ExecutorService中聲明的所有方法;
- 然後ThreadPoolExecutor繼承了類AbstractExecutorService。
- 狀態
- 在ThreadPoolExecutor中定義了一個volatile變量,另外定義了幾個static final變量表示線程池的各個狀態:volatile int runState;
- static final int RUNNING = 0;當創建線程池後,初始時,線程池處於RUNNING狀態;
- static final int SHUTDOWN = 1;如果調用了shutdown()方法,則線程池處於SHUTDOWN狀態,此時線程池不能夠接受新的任務,它會等待所有任務執行完畢;
- static final int STOP = 2;如果調用了shutdownNow()方法,則線程池處於STOP狀態,此時線程池不能接受新的任務,並且會去嘗試終止正在執行的任務;
- static final int TERMINATED = 3;當線程池處於SHUTDOWN或STOP狀態,並且所有工作線程已經銷燬,任務緩存隊列已經清空或執行結束後,線程池被設置爲TERMINATED狀態。
- 在ThreadPoolExecutor中定義了一個volatile變量,另外定義了幾個static final變量表示線程池的各個狀態:volatile int runState;
- 線程創建的三種方法:
- 1、繼承Thread類創建線程
- 2、實現Runnable接口創建線程
- 3、實現Callable接口通過FutureTask包裝器來創建Thread線程
- 4、使用ExecutorService、Callable、Future實現有返回結果的線程
- future接口:
- get獲取計算結果
- 可取消任務(任務完成後無法取消)
- FutureTask實現類:適配器模式
- FutureTask(Callable<v>,callable)運行指定的callable
- FutureTask(Runnable runnable ,v result)運行指定的Runnable,並在成功完成時get返回給定結果。其中包含定義狀態變量state,new(0)--->completing(1) ------>normal(2)正常狀態,exp(3)異常,cancell(4)取消,interrupting(5)打斷中,interrupted(已經被打斷),outcome接受返回值
- t.start()實際上是調用FutureTask中的run方法, 調用重寫的call方法,-----》set方法將狀態從0變成1,再將返回值賦值給outcome,然後狀態改變爲normal2
- 再調用get方法是時,會判斷狀態,當 狀態等於2時,將outcome返回給get
- Callable是一種可以返回結果的任務,通過適配器模式讓runnable和callable像類似,Future代表一個異步計算,可以get到計算結果,查看計算狀態。其實現FutureTask可以以被提交給Executor執行,多個線程可以得到計算結果。Callable和Future是配合使用的,當Future get到計算結果的時,如果還沒被計算出來,那麼線程將被掛起,內部使用一個單鏈表位置等待過程,當計算出結果後,等待線程解除掛起,等待線程就可以得到計算結果了
- 四種線程池?區別?線程池參數?
- Executor框架
- new Thread()缺點
- 每次new耗費性能
- new出來的線程可以無限創建,直接會互相競爭,過多佔用系統資源
- 不利於擴展(定時執行,定期執行,線程中斷)
- 採用線程池:重複使用存在的線程,減少對象的創建、消亡帶來的開銷,有效控制最大併發數,提高系統資源的使用率。同時避免過多資源競爭、避免堵塞、提供定時執行、定期執行、單線程和多線程併發控制等功能
- Executor:異步執行框架,將任務提交和運行過程解耦,基於生產者-消費者模式,用runnable表示任務,Executor還提供了對生命週期的支持,以及統計信息收集,應用程序管理機制和性能監視等機制
- Executor接口定義了最基本的Execute方法,用於接受用戶提交任務
- ExecutorService定義了線程池終止shoutdown和submit提交futureTask任務支持方法。submit提交runnable和callable,返回future可引用實例;當excute提交任務後,任務進入BlockingDeque隊列,然後由submit執行任務並返回結果
- isShutdown boolean isShutdown() 如果這個執行者已被關閉,則返回 true 。
- shutdown()啓動有序關閉,其中先前提交的任務將被執行,但不會接受任何新任務。執行完一個關閉一個,順序關閉
- isTerminated()如果所有任務在關閉後完成,則返回 true
- <T> Future<T> submit(Callable<T> task)提交值返回任務以執行,並返回代表任務待處理結果的Future。
- Future<?> submit(Runnable task)提交一個可運行的任務執行,並返回一個表示該任務的未來。
- <T> Future<T> submit(Runnable task, T result)提交一個可運行的任務執行,並返回一個表示該任務的未來。
- ThreadPoolExecutor
- 線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。
- 說明:Executors 返回的線程池對象的弊端如下:
- 1)FixedThreadPool 和 SingleThreadPool:允許的請求隊列長度爲 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。
- 2)CachedThreadPool 和 ScheduledThreadPool:允許的創建線程數量爲 Integer.MAX_VALUE,可能會創建大量的線程,從而導致 OOM。
- 說明:Executors 返回的線程池對象的弊端如下:
- 一個ExecutorService ,使用可能的幾個合併的線程執行每個提交的任務,通常使用Executors工廠方法配置。
- Executors.newCachedThreadPool() (無限線程池,具有自動線程回收) 底層的實現類還是ThreadPoolExecutor ,阻塞隊列無限127 SynchronousQueue
- Executors.newFixedThreadPool(int) (固定大小的線程池)對於ThreadPoolExecutor ,核心線程數是3,最大線程數也是3 LinkedBlockingQueue
- Executors.newSingleThreadExecutor() (單個後臺線程)LinkedBlockingQueue
- Executors.newSingleThreadScheduledExecuto(創建一個單線程執行器,可以調度命令在給定的延遲之後運行,或定期執行。 )
- 如果當前線程池中的線程數目小於corePoolSize,則每來一個任務,就會創建一個線程去執行這個任務;
- 如果當前線程池中的線程數目>=corePoolSize,則每來一個任務,會嘗試將其添加到任務緩存隊列當中,若添加成功,則該任務會等待空閒線程將其取出去執行;若添加失敗(一般來說是任務緩存隊列已滿),則會嘗試創建新的線程去執行這個任務;
- 如果當前線程池中的線程數目達到maximumPoolSize,則會採取任務拒絕策略進行處理;
- 如果線程池中的線程數量大於 corePoolSize時,如果某線程空閒時間超過keepAliveTime,線程將被終止,直至線程池中的線程數目不大於corePoolSize;如果允許爲核心池中的線程設置存活時間,那麼核心池中的線程空閒時間超過keepAliveTime,線程也會被終止。
- 線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。
- new Thread()缺點
- Executor框架
- 線程池的狀態
- 線程池的5種狀態:Running、ShutDown、Stop、Tidying、Terminated。
- 1、RUNNING
- (1) 狀態說明:線程池處在RUNNING狀態時,能夠接收新任務,以及對已添加的任務進行處理。
- (2) 狀態切換:線程池的初始化狀態是RUNNING。換句話說,線程池被一旦被創建,就處於RUNNING狀態,並且線程池中的任務數爲0!
- 2、 SHUTDOWN
- (1) 狀態說明:線程池處在SHUTDOWN狀態時,不接收新任務,但能處理已添加的任務。
- (2) 狀態切換:調用線程池的shutdown()接口時,線程池由RUNNING -> SHUTDOWN。
- 3、STOP
- (1) 狀態說明:線程池處在STOP狀態時,不接收新任務,不處理已添加的任務,並且會中斷正在處理的任務。
- (2) 狀態切換:調用線程池的shutdownNow()接口時,線程池由(RUNNING or SHUTDOWN ) -> STOP。
- 4、TIDYING
- (1) 狀態說明:當所有的任務已終止,ctl記錄的”任務數量”爲0,線程池會變爲TIDYING狀態。當線程池變爲TIDYING狀態時,會執行鉤子函數terminated()。terminated()在ThreadPoolExecutor類中是空的,若用戶想在線程池變爲TIDYING時,進行相應的處理;可以通過重載terminated()函數來實現。
- (2) 狀態切換:當線程池在SHUTDOWN狀態下,阻塞隊列爲空並且線程池中執行的任務也爲空時,就會由 SHUTDOWN -> TIDYING。當線程池在STOP狀態下,線程池中執行的任務爲空時,就會由STOP -> TIDYING。
- 5、 TERMINATED
- (1) 狀態說明:線程池徹底終止,就變成TERMINATED狀態。
- (2) 狀態切換:線程池處在TIDYING狀態時,執行完terminated()之後,就會由 TIDYING -> TERMINATED。
- workQueue的類型爲BlockingQueue<Runnable>,通常可以取下面三種類型:
- 1)ArrayBlockingQueue:基於數組的先進先出隊列,此隊列創建時必須指定大小;
- 2)LinkedBlockingQueue:基於鏈表的先進先出隊列,如果創建時沒有指定此隊列大小,則默認爲Integer.MAX_VALUE;
- 3)synchronousQueue:這個隊列比較特殊,它不會保存提交的任務,而是將直接新建一個線程來執行新來的任務。
- 線程池的任務緩存隊列已滿並且線程池中的線程數目達到maximumPoolSize,如果還有任務到來就會採取任務拒絕策略,通常有以下四種策略:
- ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。
- ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。
- ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
- ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
- 線程池容量設計
- 如果是CPU密集型任務,就需要儘量壓榨CPU,參考值可以設爲 CPU+1
- 如果是IO密集型任務,參考值可以設置爲2*CPU
- 當然,這只是一個參考值,具體的設置還需要根據實際情況進行調整,比如可以先將線程池大小設置爲參考值,再觀察任務運行情況和系統負載、資源利用率來進行適當調整。