java 中線程池(Executor)及使用場景
爲什麼需要線程池?
(1)因爲服務器如果每一個請求都會創建一個新線程,會導致性能上的瓶頸,因爲線程創建和銷燬都需要jvm不停的處理,如果一個線程執行的時間 < (線程創建時間+線程銷燬的時間)的時候,我們就要考慮線程的複用了!
(2)、線程數量創建過多,沒有有效的管理,反而起到的是副作用,會大大降低系統的性能的!
(3)、我們要根據具體的業務需求不同,結合操作系統的處理器CPU核數,能夠合理的控制線程池大小!選擇不同策略的線程池,盲目使用也會帶來一定風險,比如內存泄漏,死鎖,併發問題…
使用線程池的好處
(1)、降低資源消耗:線程複用。
(2)、提高響應速度:有任務的時候,不需要去等待創建線程,直接使用已有的線程;
(3)、管理:線程池對線程進行統一分配,調優,監控等等;
Executor接口
Executor:一個接口,其定義了一個接收Runnable對象的方法executor,其方法簽名爲executor(Runnable command),
ExecutorService:是一個比Executor使用更廣泛的子類接口,其提供了生命週期管理的方法,以及可跟蹤一個或多個異步任務執行狀況返回Future的方法
AbstractExecutorService:ExecutorService執行方法的默認實現
ScheduledExecutorService:一個可定時調度任務的接口
ScheduledThreadPoolExecutor:ScheduledExecutorService的實現,一個可定時調度任務的線程池
ThreadPoolExecutor:線程池,可以通過調用Executors以下靜態工廠方法來創建線程池並返回一個ExecutorService對象:
public ThreadPoolExecutor(int corePoolSize, // 核心線程數,如果運行的線程少於corePoolSize,則創建新線程來執行新任務,即使線程池中的其他線程是空閒的
int maximumPoolSize,// 最大線程數,可允許創建的線程數,corePoolSize和maximumPoolSize設置的邊界自動調整池大小:
// corePoolSize <運行的線程數< maximumPoolSize:僅當隊列滿時才創建新線程
// corePoolSize=運行的線程數= maximumPoolSize:創建固定大小的線程池
long keepAliveTime, // 如果線程數多於corePoolSize,則這些多餘的線程的空閒時間超過keepAliveTime時將被終止
TimeUnit unit, // keepAliveTime參數的時間單位
BlockingQueue<Runnable> workQueue, // 保存任務的阻塞隊列,與線程池的大小有關:
// 當運行的線程數少於corePoolSize時,在有新任務時直接創建新線程來執行任務而無需再進隊列
// 當運行的線程數等於或多於corePoolSize,在有新任務添加時則選加入隊列,不直接創建線程
// 當隊列滿時,在有新任務時就創建新線程
ThreadFactory threadFactory, // 使用ThreadFactory創建新線程,默認使用defaultThreadFactory創建線程
RejectedExecutionHandler handler) //後兩個參數爲可選參數:定義處理被拒絕任務的策略,默認使用ThreadPoolExecutor.AbortPolicy,任務被拒絕時將拋出RejectExecutorException
Executors類
Java線程池的靜態工廠類:Executors類,初始化4種類型的線程池:
newFixedThreadPool()
說明:初始化一個指定線程數的線程池,其中corePoolSize == maxiPoolSize,使用LinkedBlockingQuene作爲阻塞隊列
特點:即使當線程池沒有可執行任務時,也不會釋放線程。
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(new Runnable() {
@Override
public void run() {
log.info("task:{}", index);
}
});
}
executorService.shutdown();
newCachedThreadPool()
說明:初始化一個可以緩存線程的線程池,默認緩存60s,線程池的線程數可達到Integer.MAX_VALUE,即2147483647,內部使用SynchronousQueue作爲阻塞隊列;
特點:在沒有任務執行時,當線程的空閒時間超過keepAliveTime,會自動釋放線程資源;當提交新任務時,如果沒有空閒線程,則創建新線程執行任務,會導致一定的系統開銷;
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(new Runnable() {
@Override
public void run() {
log.info("task:{}", index);
}
});
}
executorService.shutdown();
因此,使用時要注意控制併發的任務數,防止因創建大量的線程導致而降低性能。
newSingleThreadExecutor()
說明:初始化只有一個線程的線程池,內部使用LinkedBlockingQueue作爲阻塞隊列。
特點:如果該線程異常結束,會重新創建一個新的線程繼續執行任務,唯一的線程可以保證所提交任務的順序執行
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(new Runnable() {
@Override
public void run() {
log.info("task:{}", index);
}
});
}
executorService.shutdown();
newScheduledThreadPool()
特定:初始化的線程池可以在指定的時間內週期性的執行所提交的任務,在實際的業務場景中可以使用該線程池定期的同步數據。除了newScheduledThreadPool的內部實現特殊一點之外,其它線程池內部都是基於ThreadPoolExecutor類(Executor的子類)實現的。
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
executorService.schedule(new Runnable() {
@Override
public void run() {
log.warn("schedule run");
}
}, 3, TimeUnit.SECONDS);
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
log.warn("schedule run");
}
}, 1, 3, TimeUnit.SECONDS);
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
log.warn("timer run");
}
}, new Date(), 5 * 1000);