爲什麼要使用線程池?
- 一個線程的執行過程包括:創建、執行、銷燬 三個階段。我們最關注的是線程執行階段。創建、銷燬線程伴隨着系統資源的開銷。線程池緩存線程,可以用已有的閒置線程來執行新任務,避免線程創建、銷燬過程帶來的系統開銷。
- 若線程併發數量過多,搶佔系統資源會導致阻塞。線程池能有效的控制西安城最大的併發數。
- 對於線程進行一些簡單的管理策略。如延遲執行、定時循環執行等策略。
1 爲什麼要用線程池?
線程池提供了一種限制和管理資源(包括執行一個任務)。 每個線程池還維護一些基本統計信息,例如已完成任務
的數量。
這裏借用《Java併發編程的藝術》提到的來說一下使用線程池的好處:
- 降低資源消耗。 通過重複利用已創建的線程降低線程創建和銷燬造成的消耗。
- 提高響應速度。 當任務到達時,任務可以不需要的等到線程創建就能立即執行。
- 提高線程的可管理性。 線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控。
2 實現Runnable接口和Callable接口的區別
如果想讓線程池執行任務的話需要實現的Runnable接口或Callable接口。 Runnable接口或Callable接口實現類都可
以被ThreadPoolExecutor或ScheduledThreadPoolExecutor執行。兩者的區別在於 Runnable 接口不會返回結果但
是 Callable 接口可以返回結果。
3 執行execute()方法和submit()方法的區別是什麼呢?
- 1) execute() 方法用於提交不需要返回值的任務,所以無法判斷任務是否被線程池執行成功與否;
- 2)submit()方法用於提交需要返回值的任務。線程池會返回一個future類型的對象,通過這個future對象可以判斷任務是否執行成功,並且可以通過future的get()方法來獲取返回值,get()方法會阻塞當前線程直到任務完成,而使用get(long timeout,TimeUnit unit) 方法則會阻塞當前線程一段時間後立即返回,這時候有可能任務沒有執行完。
4 如何創建線程池(4種線程池的區別)
Java 裏面線程池的頂級接口是Executor,但是嚴格意義上講Executor 並不是一個線程池,而只是一個執行線程的工具。真正的線程池接口是ExecutorService。
Java通過Executors提供四種線程池,分別爲:
- newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。
- newFixedThreadPool 創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
- newScheduledThreadPool 創建一個定長線程池,支持定時及週期性任務執行。
- newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
《阿里巴巴Java開發手冊》中強制線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。