轉載:https://blog.csdn.net/shakespeare001/article/details/51330745
在java.util.concurrent包下,提供了一系列與線程池相關的類,
合理的使用線程池,可以帶來多個好處:
(1)降低資源消耗。通過重複利用已建立的線程降低線程建立和銷毀造成的消耗;
(2)提高響應速度。當任務到達時,任務可以不需要等到線程建立就能立即執行;
(3)提高線程的可管理性。線程是稀缺資源,如果無限制的建立,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的配置設定,調優和監控。
線程池可以應對突然大爆發量的通路,通過有限個固定線程為大量的操作服務,減少建立和銷毀線程所需的時間。
我們一般通過工具類Executors的靜态方法(如newFixedThreadPool())來擷取ThreadPoolExecutor線程池或靜态方法(如newScheduledThreadPool())來擷取ScheduleThreadPoolExecutor線程池。如下使用:
ExecutorService threadpool= Executors.newFixedThreadPool(10);
我們指定了擷取10個數量的固定線程池。
當送出一個任務到線程池時,線程池會建立一個線程來執行任務,即使其他空閑的基本線程能夠執行新任務也會建立線程,等到需要執行的任務數大于線程池基本大小時就不再建立。如果調用了線程池的prestartAllCoreThreads方法,線程池會提前建立并啟動所有基本線程。
送出任務的兩種方式:
(1)通過execute()方法,如:
ExecutorService threadpool= Executors.newFixedThreadPool(10);
threadpool.execute(new Runnable(){...});
(2)通過submit()方法,如:
Future<?> future = threadpool.submit(new Runnable(){...});
try {
Object res = future.get();
} catch (InterruptedException e) {
// 進行中斷異常
e.printStackTrace();
} catch (ExecutionException e) {
// 處理無法執行任務異常
e.printStackTrace();
}finally{
// 關閉線程池
executor.shutdown();
}
使用submit 方法來送出任務,它會傳回一個Future對象,通過future的get方法來擷取傳回值,get方法會阻塞住直到任務完成,而使用get(long timeout, TimeUnit unit)方法則會阻塞一段時間後立即傳回,這時有可能任務沒有執行完。
從上圖我們可以看出,當送出一個新任務到線程池時,線程池的處理流程如下:
1、首先線程池判斷基本線程池是否已滿(< corePoolSize ?)?沒滿,建立一個工作線程來執行任務。滿了,則進入下個流程。
2、其次線程池判斷工作隊列是否已滿?沒滿,則将新送出的任務存儲在工作隊列裡。滿了,則進入下個流程。
3、最後線程池判斷整個線程池是否已滿(< maximumPoolSize ?)?沒滿,則建立一個新的工作線程來執行任務,滿了,則交給飽和政策來處理這個任務。
也就是說,線程池優先要建立出基本線程池大小(corePoolSize)的線程數量,沒有達到這個數量時,每次送出新任務都會直接建立一個新線程,當達到了基本線程數量後,又有新任務到達,優先放入等待隊列,如果隊列滿了,才去建立新的線程(不能超過線程池的最大數maxmumPoolSize)。