爲什麼要使用線程池
線程的創建和銷燬會帶來系統的開銷。通過線程池進行線程的管理,可以進行線程的複用,避免線程頻繁的創建和消耗。
《java併發編程的藝術》 合理利用線程池能夠帶來三個好處
- 降低資源消耗。通過重複利用已創建的線程降低線程創建和銷燬造成的消耗。
- 提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。
- 提高線程的可管理性。線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控。但是要做到合理的利用線程池,必須對其原理瞭如指掌。
線程池的屬性
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
線程池的大小
線程池的大小有三種。
-
當前線程池大小 當前線程池中的線程數量
-
核心線程池大小 corePoolSize 線程池提交一個任務。如果當前線程數量<corePoolSize,即使存在空閒線程可以執行任務也會創建一個新的線程。prestartAllCoreThreads方法可以提前創建並啓動所有核心線程。
-
最大線程池大小 maxumumPoolSize 線程池允許創建的最大線程數。如果任務隊列滿了,且當前的線程數小於最大線程池大小,則線程池會再創建新的線程執行任務。值得注意的是如果使用了無界的任務隊列這個參數就沒什麼效果。
線程存活時間 keepAliveTime
線程池的工作線程空閒後,保持存活的時間。所以如果任務很多,並且每個任務執行的時間比較短,可以調大這個時間,提高線程的利用率。
工作隊列 workQueue
用於保存等待執行的任務的阻塞隊列。 可以選擇以下幾個阻塞隊列。 ArrayBlockingQueue:是一個基於數組結構的有界阻塞隊列,此隊列按 FIFO(先進先出)原則對元素進行排序。 LinkedBlockingQueue:一個基於鏈表結構的阻塞隊列,此隊列按FIFO (先進先出) 排序元素,吞吐量通常要高於ArrayBlockingQueue。靜態工廠方法Executors.newFixedThreadPool()使用了這個隊列。 SynchronousQueue:一個不存儲元素的阻塞隊列。每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於LinkedBlockingQueue,靜態工廠方法Executors.newCachedThreadPool使用了這個隊列。 PriorityBlockingQueue:一個具有優先級的無限阻塞隊列。
線程工廠 threadFactory
用於創建線程的工廠
拒絕策略 handler
工作隊列滿了和線程池大小已經達到最大線程池大小時,說明線程池已經飽和,任務無法處理。這時通過拒絕策列處理任務。 JDK1.5提供的四種策略。 AbortPolicy:直接拋出異常。 CallerRunsPolicy:用調用者所在線程來運行任務。 DiscardOldestPolicy:丟棄隊列裏最近的一個任務,並執行當前任務。 DiscardPolicy:丟棄當前任務。 可以根據應用場景需要來實現RejectedExecutionHandler接口自定義策略
線程池執行流程
- 向線程池提交任務
- 判斷當前線程池大小是否大於核心線程池大小。如果當前線程池大小大於核心線程池大小則執行步驟3,否則創建線程執行任務。
- 判斷工作隊列是否已滿。已滿則繼續執行步驟4,否則將任務加入工作隊列,等待線程池中線程讀取隊列,執行任務。
- 判斷當前線程池大小是否大於最大線程池大小。如果當前線程池大小大於最大線程池大小則執行步驟5,否則創建線程執行任務。
- 線程池飽和,執行拒絕策列RejectedExecutionHandler.rejectedExecution。
關閉線程池 shutdown和shutdownNow
shutdown:設置線程池狀態爲SHUTDOWN,中斷所有沒有執行任務的線程。此時,則不能再往線程池中添加任何任務,否則將會拋出RejectedExecutionException異常。但是,此時線程池不會立刻退出,直到添加到線程池中的任務都已經處理完成,纔會退出。 shutdownNow:設置線程池狀態爲STOP,嘗試停止所有正在執行任務或暫停任務的線程(通過interrupt)。shutdownNow並不代表線程池就一定立即就能退出,(因爲任務如果沒有處於阻塞等待狀態,interrupt無法中斷)
它可能必須要等待所有正在執行的任務都執行完成了才能退出。
Executor框架
Thread即使工作單元也是執行機制。從jdk5起把工作單元和執行機制分開。工作單元是Runnable,Callable,執行機制由Executor框架提供。
Executor框架組成
- 任務 實現Runnable和Callable接口
- 執行機制 實現Executor接口。主要實現類由ThreadPoolExecutor和ScheduledThreadPoolExecutor。
- 異步計算結果 實現Future接口
- Executors工具類
ThreadPoolExecutor
ThreadPoolExecutor是線程池的實現。構造函數的參數前面已經介紹。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
Executors工具類提供創建ThreadPoolExecutor的方法。
CachedThreadPool
CachedThreadPool是一個可緩存的線程池。可以從ThreadPoolExecutor的構造函數參數看出它的功能。核心線程池大小0,最大線程池大小Integer.MAX_VALUE,使用不存儲元素的阻塞隊列SynchronousQueue。所以CachedThreadPool的線程池大小几乎無限大,提交任務時如果沒有空閒的線程(沒線程讀取SynchronousQueue)
就會創建一個線程。工作線程閒置60s,將會被終止。
適用:適合於大量執行時間短的任務。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
FixedThreadPool
FixedThreadPool,固定大小的線程池。核心線程池大小和最大線程池大小一致。線程存活時間爲0意味着空閒線程池不會被終止。提交任務時,都會創建一個線程。直到線程數達到指定的線程數nThreads,纔會將任務放入隊列。LinkedBlockingQueue時有界的阻塞隊列,默認大小時Integer.MAX_VALUE。
適用:執行長期的任務,負載比較重的服務器。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
SingleThreadExecutor
SingleThreadExecutor,單線程的線程池。線程池大小隻能是1.
適用:按順序執行任務,且任何時間的不會有多個線程是活動的情況。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
執行任務
線程池執行任務提供了2個方法 execute和submit
execute方法執行任務,沒返回值。
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("run");
}
});
submit方法有返回值Future,通過Future對象可以獲取返回值。
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> submit = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "run";
}
});
//阻塞獲取返回值
System.out.println(submit.get());
ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor繼承了ThreadPoolExecutor和實現了ScheduledExecutorService接口。是用於執行延時任務和定時任務的線程池。功能與Timer類似。Timer是單線程,ScheduledThreadPoolExecutor是多線程。
ScheduledExecutorService API
/**
* 延遲執行Runnable任務
*/
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
/**
* 延遲執行Callable任務
*/
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
/**
* 按固定的頻率period執行任務。initialDelay是第一次運行時的延遲時間。執行時間是 initialDelay+n*period. n是第幾次執行。
*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
/**
* 每次任務執行完後延遲時間delay後再次執行,執行時間是initalDelay+n*(任務執行時間+delay)
*/
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
使用
//通過Executors創建
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
//一秒執行一次
ScheduledFuture<?> scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}, 0, 1, TimeUnit.SECONDS);
Thread.sleep(10000);
//取消任務
scheduledFuture.cancel(true);
Future
/**
* 取消任務
* @Param mayInterruptIfRunning 是否中斷執行的任務
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* 是否已經取消
*/
boolean isCancelled();
/**
* 是否已經執行
*/
boolean isDone();
/**
* 阻塞獲取執行結果
*/
V get() throws InterruptedException, ExecutionException;
/**
* 阻塞獲取執行結果,直到超時退出阻塞狀態。
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;