[TOC]
## 常見線程池
### newFixedThreadPool
~~~
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
~~~
線程池的線程數量達corePoolSize后,即使線程池沒有可執行任務時,也不會釋放線程。
FixedThreadPool的工作隊列為無界隊列LinkedBlockingQueue(隊列容量為Integer.MAX\_VALUE), 這會導致以下問題:?
* 線程池里的線程數量不超過corePoolSize,這導致了maximumPoolSize和keepAliveTime將會是個無用參數?
* 由于使用了無界隊列, 所以FixedThreadPool永遠不會拒絕, 即飽和策略失效
### newSingleThreadExecutor
~~~
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
~~~
線程池中只有一個線程,如果該線程異常結束,會重新創建一個新的線程繼續執行任務,唯一的線程可以保證所提交任務的順序執行.
由于使用了無界隊列, 所以SingleThreadPool永遠不會拒絕, 即飽和策略失效
### newCachedThreadPool
~~~
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
~~~
1. 線程池的線程數可達到Integer.MAX\_VALUE,即2147483647,內部使用SynchronousQueue作為阻塞隊列;
2. 和newFixedThreadPool創建的線程池不同,newCachedThreadPool在沒有任務執行時,當線程的空閑時間超過keepAliveTime,會自動釋放線程資源,當提交新任務時,如果沒有空閑線程,則創建新線程執行任務,會導致一定的系統開銷;
執行過程與前兩種稍微不同:?
(1) 主線程調用SynchronousQueue的offer()方法放入task, 倘若此時線程池中有空閑的線程嘗試讀取 SynchronousQueue的task, 即調用了SynchronousQueue的poll(), 那么主線程將該task交給空閑線程. 否則執行(2)?
(2) 當線程池為空或者沒有空閑的線程, 則創建新的線程執行任務.?
(3) 執行完任務的線程倘若在60s內仍空閑, 則會被終止. 因此長時間空閑的CachedThreadPool不會持有任何線程資源.
## 構造函數三個參數
### BlockingQueue workQueue
用來保存等待被執行的任務的阻塞隊列. 在JDK中提供了如下阻塞隊列:?
1. ArrayBlockingQueue:基于數組結構的有界阻塞隊列,按FIFO排序任務;?
2. LinkedBlockingQuene:基于鏈表結構的阻塞隊列,按FIFO排序任務,吞吐量通常要高于ArrayBlockingQuene;?
3. SynchronousQuene:一個不存儲元素的阻塞隊列,每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處于阻塞狀態,吞吐量通常要高于LinkedBlockingQuene;?
4. priorityBlockingQuene:具有優先級的無界阻塞隊列;
LinkedBlockingQueue比ArrayBlockingQueue在插入刪除節點性能方面更優,但是二者在put(), take()任務的時均需要加鎖,SynchronousQueue使用無鎖算法,根據節點的狀態判斷執行,而不需要用到鎖,其核心是Transfer.transfer().
### ThreadFactory threadFactory
創建線程的工廠,通過自定義的線程工廠可以給每個新建的線程設置一個具有識別度的線程名。默認為DefaultThreadFactory
### RejectedExecutionHandler handler
線程池的飽和策略,當阻塞隊列滿了,且沒有空閑的工作線程,如果繼續提交任務,必須采取一種策略處理該任務,線程池提供了4種策略:?
1. AbortPolicy:直接拋出異常,默認策略;
2. CallerRunsPolicy:用調用者所在的線程來執行任務;
3. DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務,并執行當前任務;
4. DiscardPolicy:直接丟棄任務;?
當然也可以根據應用場景實現RejectedExecutionHandler接口,自定義飽和策略,如記錄日志或持久化存儲不能處理的任務。
## 內部狀態以及變量ctl
~~~
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
~~~
其中AtomicInteger變量ctl的功能非常強大:利用低29位表示線程池中線程數,通過高3位表示線程池的運行狀態:?
1、RUNNING:-1 << COUNT\_BITS,即高3位為111,該狀態的線程池會接收新任務,并處理阻塞隊列中的任務;?
2、SHUTDOWN: 0 << COUNT\_BITS,即高3位為000,該狀態的線程池不會接收新任務,但會處理阻塞隊列中的任務;?
3、STOP : 1 << COUNT\_BITS,即高3位為001,該狀態的線程不會接收新任務,也不會處理阻塞隊列中的任務,而且會中斷正在運行的任務;?
4、TIDYING : 2 << COUNT\_BITS,即高3位為010, 所有的任務都已經終止;?
5、TERMINATED: 3 << COUNT\_BITS,即高3位為011, terminated()方法已經執行完成?
## execute –> addWorker –>runworker (getTask)?
~~~
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
~~~
1. 如果運行的線程少于corepoolsize,請嘗試用給定的命令作為第一個線程啟動新線程任務。對AddWorker的調用自動檢查運行狀態和WorkerCount,這樣可以防止錯誤的警報不應該的時候線程,返回false。
2. 如果一個任務可以成功地排隊,那么我們仍然需要再次檢查是否應該添加一個線程(因為自上次檢查以來已有的線程已經停止了),或者自進入此方法以來,池已關閉。所以我們重新檢查狀態,必要時回滾排隊(如果停止),或者啟動新線程(如果沒有)。
3. 如果我們不能將任務排隊,那么我們嘗試添加一個新線程。如果失敗了,我們就知道我們被關閉或者飽和了,所以拒絕這個任務。
//todo
- Java
- Object
- 內部類
- 異常
- 注解
- 反射
- 靜態代理與動態代理
- 泛型
- 繼承
- JVM
- ClassLoader
- String
- 數據結構
- Java集合類
- ArrayList
- LinkedList
- HashSet
- TreeSet
- HashMap
- TreeMap
- HashTable
- 并發集合類
- Collections
- CopyOnWriteArrayList
- ConcurrentHashMap
- Android集合類
- SparseArray
- ArrayMap
- 算法
- 排序
- 常用算法
- LeetCode
- 二叉樹遍歷
- 劍指
- 數據結構、算法和數據操作
- 高質量的代碼
- 解決問題的思路
- 優化時間和空間效率
- 面試中的各項能力
- 算法心得
- 并發
- Thread
- 鎖
- java內存模型
- CAS
- 原子類Atomic
- volatile
- synchronized
- Object.wait-notify
- Lock
- Lock之AQS
- Lock子類
- 鎖小結
- 堵塞隊列
- 生產者消費者模型
- 線程池