線程池的工做主要是控制運行的線程的數量,處理過程當中將任務放入隊列,而後在線程建立後啓動這些任務,若是線程數量超過了最大數量,那麼超出數量的線程排隊等候,等其餘線程執行完畢再從隊列中取出任務來執行。java
在開發過程當中,合理地使用線程池可以帶來3個好處:數組
線程池主要處理流程:框架
ThreadPoolExecutor執行execute()方法的示意圖:ide
經過ThreadPoolExecutor來建立線程池:性能
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
建立線程池的參數:ui
1)corePoolSize
:線程池的核心線程數,定義了最小能夠同時運行的線程數量。線程
2)maximumPoolSize
:線程池的最大線程數。方隊列中存放的任務達到隊列容量時,房前能夠同時運行的線程數量變爲最大線程數。code
3)keepAliveTime
:當線程池中的線程數量大於corePoolSize時,若是沒有新任務提交,核心線程外的線程不會當即銷燬,而是會等待,直到等待的時間超過了KeepAliveTime纔會被回收銷燬。orm
4)unit
:keepAliveTime參數的時間單位,包括DAYS、HOURS、MINUTES、MILLISECONDS等。對象
5)workQueue
:用於保存等待執行任務的阻塞隊列。能夠選擇如下集個阻塞隊列:
6)threadFactory
:用於設置建立線程的工廠,能夠經過工廠給每一個創造出來的線程設置更有意義的名字。使用開源框架guava提供的ThreadFactoryBuilder能夠快速給線程池裏的線程設置有意義的名字:
new ThreadFactoryBuilder().setNameFormat("XX-task-%d").build();
7)handler
:飽和策略。若當前同時運行的線程數量達到最大線程數量而且隊列已經被放滿,ThreadPoolExecutor定義了一些飽和策略:
execute()方法用於像線程池提交不須要返回值的任務,因此沒法判斷任務是否被線程池執行成功。
executor.execute(new Runnable() { @Override public void run() { // TODO } });
submit()方法用於提交須要返回值的任務。線程池會返回一個future類型的對象,經過這個future對象能夠判斷任務是否執行成功,而且能夠經過future的get()方法獲取返回值,get()方法會阻塞當前線程直到任務完成,而使用get(long timeout, TimeUnit unit)方法則會阻塞當前線程一段時間後當即返回,這時有可能任務尚未執行完。
Future<T> future = executor.submit(hasReturnValueTask); try { T s = future.get(); } catch (InterruptedExecption | ExecutortionExcception e) { // 處理異常 e.printStackTrace(); } finally { // 關閉線程池 executor.shutdown(); }
可使用線程池的shutdown或shutdownNow方法來關閉線程池。其原理在於遍歷線程池中的工做線程,而後逐個調用線程的interrupt方法來中斷線程,因此沒法響應中斷的任務可能沒法終止。
兩者區別在於:shutdownNow方法首先將線程池狀態設置爲STOP,而後嘗試中止全部正在執行或暫停任務的線程,並返回等到執行任務的列表,而shutdown只是將線程池的狀態設置爲SHUTDOWN狀態,而後中斷全部沒有整在執行任務的線程。
查看當前設備的CPU核數:
Runtime.getRuntime().availableProcessors()
CPU密集型任務:任務須要大量的運算,而沒有阻塞,CPU一直全速運行。
CPU密集型任務配置儘量的少的線程數量。
公式:CPU核數 + 1個線程的線程池。
IO密集型任務:任務須要大量的IO,即大量的阻塞。
因爲IO密集型任務線程並非一直在執行任務,能夠多分配一點線程數,如CPU核數*2。
公式:CPU核數/(1-阻塞係數),其中阻塞係數在0.8-0.9之間。