為什麼要用線程池:
- 線程在java中是一個對象,更是作業系統的資源,線程的建立銷毀需要時間。如果建立+銷毀時間>執行任務時間就很不劃算。
- java對象占用堆記憶體,作業系統線程占用系統記憶體,根據jvm規範,一個線程預設最大棧大小為1M,這個棧空間是需要系統記憶體中配置設定的。是以線程過多,會消耗很多記憶體。
- 作業系統需要頻繁切換線程上下文,影響性能。
通過使用線程池可以控制線程數量,并且實作線程的重複利用。
線程池概念(元件)
- 線程池管理器:用于建立并管理線程池,包括建立線程,銷毀線程,添加新任務
- 工作線程:線程池中的執行任務的線程,在沒有任務時處于等待狀态,可以循環的執行任務
- 任務接口:每個任務必須實作的接口,以供工作線程排程任務的運作,它主要規定了任務的入口,任務執行完後的收尾工作,任務的執行狀态等
- 任務隊列:用于存放沒有被處理的任務,提供一種緩沖機制。
線程池接口定義和實作類
- 接口 Executor 最上層的接口,定義了執行任務的方法execute(Runnable))
- 接口 ExecutorService 繼承了Executor接口,拓展了Callable、Future、關閉等相關方法
- 接口 ScheduledExecutorService 繼承了ExecutorService,增加了定時任務相關方法
- 實作類 ThreadPoolExecutor 基礎、标準的線程池實作
- 實作類 ScheduledTreadPoolExecutor 繼承了ThreadPoolExecutor,實作了ScheduledExecutorService中定時任務相關方法
标準線程池
參數:
- corePoolSize(int):核心線程數
- maximumPoolSize(int):最大線程數
- keepAliveTime(long):超出核心線程數的線程等待存活時間
- unit(TimeUnit):keepAliveTime時間機關
- workQueue(BlockingQueue<Runnable>):任務隊列
- threadFactury(ThreadFactory):線程建立工廠
- handler(RejectedExecutionHandler):拒絕政策
任務執行流程:
- 是否達到核心線程數量?如果沒有達到,建立一個工作線程來執行任務
- 工作隊列是否已滿?如果沒滿,則将新送出的任務存儲在工作隊列裡
- 是否達到線程池最大數量?如果沒達到,則建立一個新的工作線程來執行任務
- 執行自定義或預設的抛出RejectedExecutionException拒絕政策來處理任務
定時任務線程池
參數:
- corePoolSize(int) 核心線程數
- threadFactory(ThreadFactory) 線程建立工廠
- handler(RejectedExecutionHandler) 拒絕政策
最大線程數為Integer.MAX_VALUE,存活時間為0,使用DelayWorkQueue延時隊列作為延時任務存放隊列。
定時任務使用方式:
scheduleAtFixedRate、scheduleWithFixedDelay
線程池的關閉
shutdown:線程池被關閉後,如果送出新任務,則會直接執行拒絕政策,但是會等待已有任務全部執行完成
shutdownNow:線程池被關閉後,無法送出新任務,并且會嘗試中斷有所正在執行的任務,傳回等待的任務清單
控制線程數量
一般使CPU使用率達到80%
計算行任務:cpu數量的1-2倍
I/O型任務:可以使用更多的線程,根據具體的I/O阻塞時長決定