一個線程池管理了一組工作線程,同時它還包括了一個用於放置等待執行的任務的隊列。線程池可以避免線程的頻繁創建與銷燬,降低資源的消耗,提高系統的反應速度。java.util.concurrent.Executors提供了幾個java.util.concurrent.Executor接口的實現用於創建線程池,其主要涉及四個角色:
- 線程池:Executor
- 工作線程:Worker線程,Worker的run()方法執行Job的run()方法
- 任務Job:Runable和Callable
- 阻塞隊列:BlockingQueue
一、 線程池Executor
Executor及其實現類是用戶級的線程調度器,也是對任務執行機制的抽象,其將任務的提交與任務的執行分離開來,核心實現類包括ThreadPoolExecutor(用來執行被提交的任務)和ScheduledThreadPoolExecutor(可以在給定的延遲後執行任務或者週期性執行任務)。
Executor的實現繼承鏈條爲:(父接口)Executor -> (子接口)ExecutorService -> (實現類)[ ThreadPoolExecutor + ScheduledThreadPoolExecutor ]。
二、任務Runable/Callable
Runnable(run)和Callable(call)都是對任務的抽象,但是Callable可以返回任務執行的結果或者拋出異常。
三、任務執行狀態Future
Future是對任務執行狀態和結果的抽象,核心實現類是furtureTask (所以它既可以作爲Runnable被線程執行,又可以作爲Future得到Callable的返回值) ;
(1). 使用Callable+Future獲取執行結果
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
Future<Integer> result = executor.submit(task);
System.out.println("task運行結果" + result.get());
class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("子線程在進行計算");
Thread.sleep(3000);
int sum = 0;
for(int i=0;i<100;i++)
sum += i;
return sum;
}
(2). 使用Callable + FutureTask獲取執行結果
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
executor.submit(futureTask);
System.out.println("task運行結果"+futureTask.get());
class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("子線程在進行計算");
Thread.sleep(3000);
int sum = 0;
for(int i=0;i<100;i++)
sum += i;
return sum;
}
四、四種常用的線程池
(1). FixedThreadPool
用於創建使用固定線程數的ThreadPool,corePoolSize = maxPoolSize = n(固定的含義),阻塞隊列爲LinkedBlockingQueue。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
(2). SingleThreadExecutor
用於創建一個單線程的線程池,corePoolSize = maxPoolSize = 1,阻塞隊列爲LinkedBlockingQueue。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
(3). CachedThreadPool
用於創建一個可緩存的線程池,corePoolSize = 0, maxPoolSize = Integer.MAX_VALUE,阻塞隊列爲SynchronousQueue(沒有容量的阻塞隊列,每個插入操作必須等待另一個線程對應的移除操作,反之亦然),是根據需求創建新線程的,需求多時,創建的就多,需求少時,JVM自己會慢慢的釋放掉多餘的線程。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
(4). ScheduledThreadPoolExecutor
用於創建一個大小無限的線程池,此線程池支持定時以及週期性執行任務的需求。
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
new DelayedWorkQueue());
}
五、線程池的飽和策略
當阻塞隊列滿了,且沒有空閒的工作線程,如果繼續提交任務,必須採取一種策略處理該任務,線程池提供了4種策略:
- AbortPolicy:直接拋出異常,默認策略;
- CallerRunsPolicy:用調用者所在的線程來執行任務;
- DiscardOldestPolicy:丟棄阻塞隊列中最老的任務,並執行當前任務;
- DiscardPolicy:直接丟棄任務;
當然也可以根據應用場景實現RejectedExecutionHandler接口,自定義飽和策略,如記錄日誌或持久化存儲不能處理的任務。
六、 線程池調優
- 設置最大線程數,防止線程資源耗盡;
- 使用有界隊列,從而增加系統的穩定性和預警能力(飽和策略);
- 根據任務的性質設置線程池大小:CPU密集型任務(CPU個數個線程),IO密集型任務(CPU個數兩倍的線程),混合型任務(拆分)。