線程模型
用戶線程(UTL):由應用去管理線程,不需要用戶態-內核態切換。
內核線程(KTL):創建線程在任務管理器中可見,java創建的線程由操作系統管理,操作系統對應一個內核空間線程,線程和內核線程一一對應。
private native void start0();
java的線程是KTL內核線程模型。關鍵代碼,Thread類中創建線程,是由本地方法庫中的start0方法創建線程。
線程狀態:
private final AtomicInteger ctl = new AtomicInteger(RUNNING);
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1; // 2^29
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS; // 111
private static final int SHUTDOWN = 0 << COUNT_BITS; // 000
private static final int STOP = 1 << COUNT_BITS; // 001
private static final int TIDYING = 2 << COUNT_BITS; // 010
private static final int TERMINATED = 3 << COUNT_BITS; // 011
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; }
// 運算結果(高3位爲狀態、低29位爲數量)
COUNT_BITS:29
CAPACITY :000 11111111111111111111111111111
RUNNING :111 00000000000000000000000000000
SHUTDOWN :000 00000000000000000000000000000
STOP :001 00000000000000000000000000000
TIDYING :010 00000000000000000000000000000
TERMINATED:011 00000000000000000000000000000
ctl:記錄活動線程的數量(低29位)、線程池的狀態(高3位)【Integer.SIZE共32位】
CAPACITY=0 :初始容量
COUNT_BITS:29 =(Integer.SIZE=32)-3,
RUNNING(111):接受新任務、可以處理已添加的任務。
SHUTDOWN(000):不接受新任務、可以處理已添加的任務。
STOP(001):不接受新任務、不處理已添加的任務、並且中斷正在處理的任務。
TIDYING(010):所有的任務已經終止,ctl數=0。
TERMINATED(011):線程池終止。
構造函數解析
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- acc : 獲取調用上下文
- corePoolSize: 核心線程數量,規定線程池有幾個線程(worker)在運行。
- maximumPoolSize: 最大的線程數量。當workQueue滿了,不能添加任務的時候,這個參數纔會生效。規定線程池中最多有多少個線程(worker)在執行
- workQueue:存放任務的隊列。
- unit: 生存時間對於的單位
- keepAliveTime:超出corePoolSize大小的那些線程的生存時間,這些線程如果長時間沒有執行任務並且超過了該keepAliveTime設定的時間,就會消亡。
- threadFactory: 創建線程的工廠,在這個地方可以統一處理創建的線程的屬性。
- handler:當workQueue已經滿了,並且線程池線程數已經達到maximumPoolSize,將執行拒絕策略。
線程池執行流程
// 執行任務方法
public void execute(Runnable command) {
// 1、小於核心線程數,直接創建線程執行任務addWorker(command, true)
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2、大於核心線程數,嘗試加入隊列,進行雙重檢測線程池運行狀態
// 創建非核心線程執行任務addWorker(null, false)
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);
}
// 3、嘗試入隊失敗,addWorker(command, false)在自旋時狀態檢測返回false,未創建Worker對象
// 則執行拒絕策略
else if (!addWorker(command, false))
reject(command);
}
// 添加worker對象到workers集合中
private boolean addWorker(Runnable firstTask, boolean core) {
//
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 1、檢測線程池的狀態,如果關閉了則不再添加worker,直接返回false
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 2、自旋的方式對活動的線程數workercount+1
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 3、封裝worker對象,內部創建了新的線程,w.thread即可獲取該線程
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
// 4、通過線程池的重入鎖機制,將worker加入workers集合中,等待workers執行。
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 5、加入成功後,調用該線程執行。
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
// 加入線程池失敗,則內部通過自旋的方式,將活動線程數workerCount-1
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
Worker(Runnable firstTask) {
setState(-1);
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this); // 1、此處將worker對象本身加入線程
}
public void run() { // 2、由於worker對象實現Runnable,由1處的thread執行start時,會調用該方法
runWorker(this); // 3、該runWorker方法爲線程池的runWorker方法。
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
// 4、此處只留下了關鍵代碼,如果firstTask==null,則通過getTask方法從隊列中獲取任務。
while (task != null || (task = getTask()) != null) {
// 5、執行任務
task.run();
}
}
線程集合:HashSet:workers :不斷的從workQueue隊列中獲取線程,執行任務
阻塞隊列:BlockingQueue:workQueue:存放線程任務的隊列,FIFO一端入隊一端出隊
流程原理:
1、用戶向線程池中提交線程任務,執行execute方法。
2、如果當前線程池中的線程數量workerCount小於核心線程數corepoolsize
2.1、通過自旋的方式對workerCount數量做CAS進行+1,如果當前活動線程>corepoolsize或者maxinumpoolsize,則自旋失敗,返回false,如果成功,則開始封裝Worker對象
2.2、通過線程任務封裝Worker對象時,先獲取線程池的重入鎖,獲取鎖後判斷當前線程池的狀態,如果爲001、010、011狀態,則操作失敗,會對workerCount數量CAS做-1,同時停止線程池,返回false。
2.3、線程池將該線程封裝成Worker對象,添加到workers執行。
3、如果當前線程池中的線程數量workerCount達到了corepoolsize,則將任務加入workQueue隊列中。
4、如果隊列已經滿,但未達到maxinumpoolsize數量,新建(非核心)線程執行任務。
4.1、由addWorker(command, false),添加的firstTask爲null,所以封裝的Worker對象的firstTask爲null,
4.2、由Worker對象執行線程,由於firstTask==null,所以不斷循環的從workQueue隊列中獲取任務步驟3加入的任務執行。
4.3、執行完成後對該任務標記completedTaskCount已完成數量。同時移除該任務。
5、如果隊列已經滿,總線程數達到了maxinumpoolsize數量,會由RejectedExecutionHandler執行拒絕策略。
默認線程池:
newFixedThreadPool:指定線程的線程池,核心數=最大數,不會釋放線程
newCachedThreadPool:可緩存60秒線程的線程池,核心數=0,最大數=Integer.MAX會自動釋放線程
newSingleThreadExecutor:只有一個線程,核心數=最大數=1,可以保證線程的任務順序執行
newScheduledThreadPool:可以指定時間、週期性執行提交的任務線程
默認任務隊列:
1、ArrayBlockingQueue:基於數組結構的有界阻塞隊列,按FIFO排序任務;
2、LinkedBlockingQuene:基於鏈表結構的阻塞隊列,按FIFO排序任務,吞吐量通常要高於ArrayBlockingQuene;
3、SynchronousQuene:一個不存儲元素的阻塞隊列,每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於LinkedBlockingQuene;
4、PriorityBlockingQuene:具有優先級的無界阻塞隊列;
默認拒絕策略:
1、AbortPolicy:直接拋出異常,默認策略;
2、CallerRunsPolicy:用調用者所在的線程來執行任務;
3、DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務,並執行當前任務;
4、DiscardPolicy:直接丟棄任務;
當然也可以根據應用場景實現RejectedExecutionHandler接口,自定義飽和策略,如記錄日誌或持久化存儲不能處理的任務。
參考資料:
https://blog.csdn.net/u013332124/article/details/79587436
https://www.jianshu.com/p/87bff5cc8d8c
https://www.cnblogs.com/DDiamondd/p/11362164.html