[TOC]
# 線程池的實現原理
## 4種實現線程池的方式
有4種實現線程池的方式`newFixedThreadPool`、`newCachedThreadPoo`l、`newSingleThreadExecutor`、`newScheduledThreadPool`;
`newFixedThreadPool`:創建一個指定線程數的線程池,當corePoolSize == maximumPoolSize時,使用LinkedBlockingQuene作為阻塞隊列,不過當線程池沒有可執行任務時,也不會釋放線程。
`newCachedThreadPool`:初始化一個可以緩存線程的線程池,使用SynchronousQueue作為阻塞隊列,當線程的空閑時間超過keepAliveTime,會自動釋放線程資源,
當提交新任務時,如果沒有空閑線程,則創建新線程執行任務,會導致一定的系統開銷;需控制并發任務數,否則會創建大量的線程
`newSingleThreadExecutor`:初始化的線程池中只有一個線程,如果該線程異常結束,會重新創建一個新的線程繼續執行任務,唯一的線程可以保證所提交任務的順序執行,內部使用LinkedBlockingQueue作為阻塞隊列。
`newScheduledThreadPool`:初始化的線程池可以在指定的時間內周期性的執行所提交的任務,在實際的業務場景中可以使用該線程池定期的同步數據。
前三種實現方式 new TreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
corePoolSize:線程池中核心線程數;
maximumPoolSize:線程池中允許的最大線程數;
keepAliveTime:線程空閑時的存活時間,即當線程沒有任務執行時,繼續存活的時間;
TimeUnit: keepAliveTime的單位;
workQueue:阻塞隊列,有四種結構的阻塞隊列
(1)ArrayBlockingQueue:基于數組結構的有界阻塞隊列,按FIFO排序任務;
(2)LinkedBlockingQuene:基于鏈表結構的阻塞隊列,按FIFO排序任務,吞吐量通常要高于ArrayBlockingQuene;
(3)SynchronousQuene:一個不存儲元素的阻塞隊列,每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處于阻塞狀態,吞吐量通常要高于LinkedBlockingQuene;
(4)priorityBlockingQuene:具有優先級的無界阻塞隊列;
## threadFactory:創建線程的工廠
handler:線程池的飽和策略,當阻塞隊列滿了,且沒有空閑的工作線程,如果繼續提交任務,必須采取一種策略處理該任務,線程池提供了4種策略,也可以自定義飽和策略,如記錄日志或持久化存儲不能處理的任務
(1)AbortPolicy:直接拋出異常,默認策略;
(2)CallerRunsPolicy:用調用者所在的線程來執行任務;
(3)DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務,并執行當前任務;
(4)DiscardPolicy:直接丟棄任務
任務提交:兩種提交方式Executor.execute()、ExecutorService.submit()
`void Executor.execute()`:必須實現Runnable接口,沒有返回值,無法判斷任務是否執行成功;
`<T> Future<T> ExecutorService.submit()`:可以獲取任務執行完的返回值。
## Executor.execute()線程池處理任務過程:
(1)獲取線程池中當前線程池數量,如果當前線程池數量小于coolPoolSize,則創建新的線程執行任務;
(2)如果線程池處于running狀態,并且提交的任務成功放到了阻塞隊列中,則執行步驟(3),否則執行步驟(4);
(3)再次檢查線程池的狀態,如果線程池狀態不是running,并且成功將任務移除阻塞隊列,則執行reject方法處理任務
(4)創建新的線程執行任務,如果執行失敗,執行reject方法處理任務
## 線程池是怎樣實現創建新線程并執行任務的:
(1)判斷線程池的狀態,如果線程池的狀態值大于或等SHUTDOWN,則不處理提交的任務,直接返回;
(2)判斷當前需要創建的線程是否是核心線程,如果是核心線程,且線程數小于coolPoolSize,則開始創建新線程;
(3)開始創建線程:線程池的工作線程通過Woker類實現,在ReentrantLock鎖的保證下,把Woker實例插入到HashSet后,并調用start()方法啟動Woker中的線程,執行runWorker方法。
(4)runWorker方法是線程池的核心:
a.通過unlock方法釋放鎖;
b.獲取第一個任務firstTask,執行任務的run方法,不過在執行任務之前,會進行加鎖操作,任務執行完會釋放鎖;
c.firstTask執行完成之后,通過getTask方法從阻塞隊列中獲取等待的任務,如果阻塞隊列中沒有任務,getTask方法會被阻塞并掛起,不會占用cpu資源;
d.當隊列中有任務加入時,線程被喚醒,并返回任務去執行,如果在keepAliveTime時間內,阻塞隊列還是沒有任務,則返回null;
## ExecutorService.submit()線程池處理任務過程:
(1)在實際業務場景中,Future和Callable基本是成對出現的,Callable負責產生結果,Future負責獲取結果
```
private ExecuteService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args){
Future<String> future = executor.submit(new Task());
String result = future.get();
}
static class Task implements Callable<String>{
@override
public String call(){
return "success";
}
}
```
(2)Callable接口類似于Runnable,只是Runnable沒有返回值。
(3)Callable任務除了返回正常結果之外,如果發生異常,該異常也會被返回,即Future可以拿到異步執行任務各種結果;
(4)Future.get方法會導致主線程阻塞,直到Callable任務執行完成;