在Java開發中,經常需要創建執行緒去執行一些任務,實作起來也非常方便,但如果并發的執行緒數量很多,并且每個執行緒都是執行一個時間很短的任務就結束了,這樣頻繁創建執行緒就會大大降低系統的效率,因為頻繁創建執行緒和銷毀執行緒需要時間,此時,我們很自然會想到使用執行緒池來解決這個問題,
使用執行緒池的好處:
降低資源消耗,java中所有的池化技術都有一個好處,就是通過復用池中的物件,降低系統資源消耗,設想一下如果我們有n多個子任務需要執行,如果我們為每個子任務都創建一個執行執行緒,而創建執行緒的程序是需要一定的系統消耗的,最后肯定會拖慢整個系統的處理速度,而通過執行緒池我們可以做到復用執行緒,任務有多個,但執行任務的執行緒可以通過執行緒池來復用,這樣減少了創建執行緒的開銷,系統資源利用率得到了提升,
降低管理執行緒的難度,多執行緒環境下對執行緒的管理是最容易出現問題的,而執行緒池通過框架為我們降低了管理執行緒的難度,我們不用再去擔心何時該銷毀執行緒,如何最大限度的避免多執行緒的資源競爭,這些事情執行緒池都幫我們代勞了,
提升任務處理速度,執行緒池中長期駐留了一定數量的活執行緒,當任務需要執行時,我們不必先去創建執行緒,執行緒池會自己選擇利用現有的活執行緒來處理任務,
很顯然,執行緒池一個很顯著的特征就是“長期駐留了一定數量的活執行緒”,避免了頻繁創建執行緒和銷毀執行緒的開銷,那么它是如何做到的呢?我們知道一個執行緒只要執行完了run()方法內的代碼,這個執行緒的使命就完成了,等待它的就是銷毀,既然這是個“活執行緒”,自然是不能很快就銷毀的,為了搞清楚這個“活執行緒”是如何作業的,下面通過追蹤原始碼來看看能不能解開這個疑問,
學習過執行緒池都知道,可以通過工廠類Executors來創個多種型別的執行緒池,部分型別如下:
public static ExecutorService newFixedThreadPool(int var0) {
return new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
}
public static ExecutorService newSingleThreadExecutor() {
return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());
}
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new Executors.DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1));
}
public static ScheduledExecutorService newScheduledThreadPool(int var0) {
return new ScheduledThreadPoolExecutor(var0);
}
無論哪種型別的執行緒池,最終都是直接或者間接通過ThreadPoolExecutor這個類來實作的,而ThreadPoolExecutor的有多個構造方法,最終都是呼叫含有7個引數的建構式,
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
① corePoolSize
顧名思義,其指代核心執行緒的數量,當提交一個任務到執行緒池時,執行緒池會創建一個核心執行緒來執行任務,即使其他空閑的核心執行緒能夠執行新任務也會創建新的核心執行緒,而等到需要執行的任務數大于執行緒池核心執行緒的數量時就不再創建,這里也可以理解為當核心執行緒的數量等于執行緒池允許的核心執行緒最大數量的時候,如果有新任務來,就不會創建新的核心執行緒,
如果你想要提前創建并啟動所有的核心執行緒,可以呼叫執行緒池的prestartAllCoreThreads()方法,
② maximumPoolSize
顧名思義,其指代執行緒池允許創建的最大執行緒數,如果佇列滿了,并且已創建的執行緒數小于最大執行緒數,則執行緒池會再創建新的執行緒執行任務,所以只有佇列滿了的時候,這個引數才有意義,因此當你使用了無界任務佇列的時候,這個引數就沒有效果了,
③ keepAliveTime
顧名思義,其指代執行緒活動保持時間,即當執行緒池的作業執行緒空閑后,保持存活的時間,所以,如果任務很多,并且每個任務執行的時間比較短,可以調大時間,提高執行緒的利用率,不然執行緒剛執行完一個任務,還沒來得及處理下一個任務,執行緒就被終止,而需要執行緒的時候又再次創建,剛創建完不久執行任務后,沒多少時間又終止,會導致資源浪費,
注意:這里指的是核心執行緒池以外的執行緒,還可以設定allowCoreThreadTimeout = true這樣就會讓核心執行緒池中的執行緒有了存活的時間,
④ TimeUnit
顧名思義,其指代執行緒活動保持時間的單位:可選的單位有天(DAYS)、小時(HOURS)、分鐘(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和納秒(NANOSECONDS,千分之一微秒),
⑤ workQueue
顧名思義,其指代任務佇列:用來保存等待執行任務的阻塞佇列,
⑥ threadFactory
顧名思義,其指代創建執行緒的工廠:可以通過執行緒工廠給每個創建出來的執行緒設定更加有意義的名字,
⑦ RejectedExecutionHandler
顧名思義,其指代拒絕執行程式,可以理解為飽和策略:當佇列和執行緒池都滿了,說明執行緒池處于飽和狀態,那么必須采取一種策略處理提交的新任務,這個策略默認情況下是AbortPolicy,表示無法處理新任務時拋出例外,在JDK1.5中Java執行緒池框架提供了以下4種策略,
AbortPolicy:直接拋出例外RejectedExecutionException,
CallerRunsPolicy:只用呼叫者所在執行緒來運行任務,即由呼叫 execute方法的執行緒執行該任務,
DiscardOldestPolicy:丟棄佇列里最近的一個任務,并執行當前任務,
DiscardPolicy:不處理,丟棄掉,即丟棄且不拋出例外,
這7個引數共同決定了執行緒池執行一個任務的策略:
當一個任務被添加進執行緒池時:
-
執行緒數量未達到 corePoolSize,則新建一個執行緒(核心執行緒)執行任務
-
執行緒數量達到了 corePools,則將任務移入佇列等待
-
佇列已滿,新建執行緒(非核心執行緒)執行任務
-
佇列已滿,總執行緒數又達到了 maximumPoolSize,就會由上面那位星期天(RejectedExecutionHandler)拋出例外
說白了就是先利用核心執行緒,核心執行緒用完,新來的就加入等待佇列,一旦佇列滿了,那么只能開始非核心執行緒來執行了,
上面的策略,會在閱讀代碼的時候體現出來,并且在代碼中也能窺探出真正復用空閑執行緒的實作原理,
接下來我們就從執行緒池執行任務的入口分析,
一個執行緒池可以接受任務型別有Runnable和Callable,分別對應了execute和submit方法,目前我們只分析execute的執行程序,
上原始碼:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
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);//啟動非核心執行緒執行,注意這里任務是null,其實里面會去取任務佇列里的任務執行
}
else if (!addWorker(command, false))//第三步:加入不了佇列(即佇列滿了),嘗試啟動非核心執行緒
reject(command);//如果啟動不了非核心執行緒執行,說明到達了最大執行緒數量的限制,會使用第7個引數拋出例外
}
代碼并不多,主要分三個步驟,其中有兩個靜態方法經常被用到,主要用來判斷執行緒池的狀態和有效執行緒數量:
// 獲取運行狀態
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 獲取活動執行緒數
private static int workerCountOf(int c) { return c & CAPACITY; }
總結一下,execute的執行邏輯就是:
-
如果 當前活動執行緒數 < 指定的核心執行緒數,則創建并啟動一個執行緒來執行新提交的任務(此時新建的執行緒相當于核心執行緒);
-
如果 當前活動執行緒數 >= 指定的核心執行緒數,且快取佇列未滿,則將任務添加到快取佇列中;
-
如果 當前活動執行緒數 >= 指定的核心執行緒數,且快取佇列已滿,則創建并啟動一個執行緒來執行新提交的任務(此時新建的執行緒相當于非核心執行緒);
從代碼中我們也可以看出,即便當前活動的執行緒有空閑的,只要這個活動的執行緒數量小于設定的核心執行緒數,那么依舊會啟動一個新執行緒來執行任務,也就是說不會去復用任何執行緒,在execute方法里面我們沒有看到執行緒復用的影子,那么我們繼續來看看addWorker方法,
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
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;
// else CAS failed due to workerCount change; retry inner loop
}
}
//前面都是執行緒池狀態的判斷,暫時不理會,主要看下面兩個關鍵的地方
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask); // 新建一個Worker物件,這個物件包含了待執行的任務,并且新建一個執行緒
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
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();
}
if (workerAdded) {
t.start(); // 啟動剛創建的worker物件里面的thread執行
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
方法雖然有點長,但是我們只考慮兩個關鍵的地方,先是創建一個worker物件,創建成功后,對執行緒池狀態判斷成功后,就去執行該worker物件的thread的啟動,也就是說在這個方法里面啟動了一個關聯到worker的執行緒,但是這個執行緒是如何執行我們傳進來的runnable任務的呢?接下來看看這個Worker物件到底做了什么,
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker. */
public void run() {
runWorker(this);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
最重要的構造方法:
Worker(Runnable firstTask) { // worker本身實作了Runnable介面
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask; // 持有外部傳進來的runnable任務
//創建了一個thread物件,并把自身這個runnable物件給了thread,一旦該thread執行start方法,就會執行worker的run方法
this.thread = getThreadFactory().newThread(this);
}
在addWorker方法中執行的t.start會去執行worker的run方法:
public void run() {
runWorker(this);
}
run方法又執行了ThreadPoolExecutor的runWorker方法,把當前worker物件傳入,
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask; // 取出worker的runnable任務
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 回圈不斷的判斷任務是否為空,當第一個判斷為false的時候,即task為null,這個task啥時候為null呢?
// 要么w.firstTask為null,還記得我們在execute方法第二步的時候,執行addWorker的時候傳進來的runnable是null嗎?
// 要么是執行了一遍while回圈,在下面的finally中執行了task=null;
// 或者執行第二個判斷,一旦不為空就會繼續執行回圈里的代碼,
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run(); // 任務不為空,就會執行任務的run方法,也就是runnable的run方法
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null; // 執行完成置null,繼續下一個回圈
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
方法比較長,歸納起來就三步:
1,從worker中取出runnable(這個物件有可能是null,見注釋中的解釋);
2,進入while回圈判斷,判斷當前worker中的runnable,或者通過getTask得到的runnable是否為空,不為空的情況下,就執行run;
3,執行完成把runnable任務置為null,
假如我們不考慮此方法里面的while回圈的第二個判斷,在我們的執行緒開啟的時候,順序執行了runWorker方法后,當前worker的run就執行完成了,
既然執行完了那么這個執行緒也就沒用了,只有等待虛擬機銷毀了,那么回顧一下我們的目標:Java執行緒池中的執行緒是如何被重復利用的?好像并沒有重復利用啊,新建一個執行緒,執行一個任務,然后就結束了,銷毀了,沒什么特別的啊,難道有什么地方漏掉了,被忽略了?
仔細回顧下該方法中的while回圈的第二個判斷(task = getTask)!=null
玄機就在getTask方法中,
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// timed變數用于判斷是否需要進行超時控制,
// allowCoreThreadTimeOut默認是false,也就是核心執行緒不允許進行超時;
// wc > corePoolSize,表示當前執行緒池中的執行緒數量大于核心執行緒數量;
// 對于超過核心執行緒數量的這些執行緒或者允許核心執行緒進行超時控制的時候,需要進行超時控制
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 如果需要進行超時控制,且上次從快取佇列中獲取任務時發生了超時(timedOut開始為false,后面的回圈末尾超時時會置為true)
// 或者當前執行緒數量已經超過了最大執行緒數量,那么嘗試將workerCount減1,即當前活動執行緒數減1,
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
// 如果減1成功,則回傳null,這就意味著runWorker()方法中的while回圈會被退出,其對應的執行緒就要銷毀了,也就是執行緒池中少了一個執行緒了
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 注意workQueue中的poll()方法與take()方法的區別
//poll方式取任務的特點是從快取佇列中取任務,最長等待keepAliveTime的時長,取不到回傳null
//take方式取任務的特點是從快取佇列中取任務,若佇列為空,則進入阻塞狀態,直到能取出物件為止
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true; // 能走到這里說明已經超時了
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
注釋已經很清楚了,getTask的作用就是,在當前執行緒中:
1,如果當前執行緒池執行緒數量大于核心執行緒數量或者設定了對核心執行緒進行超時控制的話(此時相當于對所有執行緒進行超時控制),就會去任務佇列獲取超時時間內的任務(佇列的poll方法),獲取到的話就會繼續執行任務,也就是執行runWorker方法中的while回圈里的任務的run方法,執行完成后,又繼續進入getTask從任務佇列中獲取下一個任務,如果在超時時間內沒有獲取到任務,就會走到getTask的倒數第三行,設定timeOut標記為true,此時繼續進入getTask的for回圈中,由于超時了,那么就會進入嘗試去去對執行緒數量-1操作,-1成功了,就直接回傳一個null的任務,這樣就回到了當前執行緒執行的runWorker方法中,該方法的while回圈判斷getTask為空,直接退出回圈,這樣當前執行緒就執行完成了,意味著要被銷毀了,這樣自然就會被回收器擇時回收了,也就是執行緒池中少了一個執行緒了,因此只要執行緒池中的執行緒數大于核心執行緒數(或者核心執行緒也允許超時)就會這樣一個一個地銷毀這些多余的執行緒,
2,如果當前活動執行緒數小于等于核心執行緒數(或者不允許核心執行緒超時),同樣也是去快取佇列中取任務,但當快取佇列中沒任務了,就會進入阻塞狀態(佇列的take方法),直到能取出任務為止(也就是佇列中被新添加了任務時),因此這個執行緒是處于阻塞狀態的,并不會因為快取佇列中沒有任務了而被銷毀,這樣就保證了執行緒池有N個執行緒是活的,可以隨時處理任務,從而達到重復利用的目的,
綜上所述,執行緒之所以能達到復用,就是在當前執行緒執行的runWorker方法中有個while回圈,while回圈的第一個判斷條件是執行當前執行緒關聯的Worker物件中的任務,執行一輪后進入while回圈的第二個判斷條件getTask(),從任務佇列中取任務,取這個任務的程序要么是一直阻塞的,要么是阻塞一定時間直到超時才結束的,超時到了的時候這個執行緒也就走到了生命的盡頭,
然而在我們開始分析execute的時候,這個方法中的三個部分都會呼叫addWorker去執行任務,在addWorker方法中都會去新建一個執行緒來執行任務,這樣的話是不是每次execute都是去創建執行緒了?事實上,復用機制跟執行緒池的阻塞佇列有很大關系,我們可以看到,在execute在核心執行緒滿了,但是佇列不滿的時候會把任務加入到佇列中,一旦加入成功,之前被阻塞的執行緒就會被喚醒去執行新的任務,這樣就不會重新創建執行緒了,
我們用個例子來看下:
假設我們有這么一個ThreadPoolExecutor,核心執行緒數設定為5(不允許核心執行緒超時),最大執行緒數設定為10,超時時間為20s,執行緒佇列是LinkedBlockingDeque(相當于是個無界佇列),
當我們給這個執行緒池陸續添加任務,前5個任務執行的時候,會執行到我們之前分析的execute方法的第一步部分,會陸續創建5個執行緒做為核心執行緒執行任務,當前執行緒里面的5個關聯的任務執行完成后,會進入各自的while回圈的第二個判斷getTask中去取佇列中的任務,假設當前沒有新的任務過來也就是沒有執行execute方法,那么這5個執行緒就會在workQueue.take()處一直阻塞的,這個時候,我們執行execute加入一個任務,即第6個任務,這個時候會進入execute的第二部分,將任務加入到佇列中,一旦加入佇列,之前阻塞的5個執行緒其中一個就會被喚醒取出新加入的任務執行了,(這里有個execute的第二部分的后半段執行重復校驗的代碼即addWorker(傳入null任務),目前還沒搞明白是怎么回事),
在我們這個例子中,由于佇列是無界的,所以始終不會執行到execute的第三部分即啟動非核心執行緒,假如我們設定佇列為有界的,那么必然就會執行到這里了,
小結
通過以上的分析,應該算是比較清楚地解答了“執行緒池中的核心執行緒是如何被重復利用的”這個問題,同時也對執行緒池的實作機制有了更進一步的理解:
當有新任務來的時候,先看看當前的執行緒數有沒有超過核心執行緒數,如果沒超過就直接新建一個執行緒來執行新的任務,如果超過了就看看快取佇列有沒有滿,沒滿就將新任務放進快取佇列中,滿了就新建一個執行緒來執行新的任務,如果執行緒池中的執行緒數已經達到了指定的最大執行緒數了,那就根據相應的策略拒絕任務,
當快取佇列中的任務都執行完了的時候,執行緒池中的執行緒數如果大于核心執行緒數,就銷毀多出來的執行緒,直到執行緒池中的執行緒數等于核心執行緒數,此時這些執行緒就不會被銷毀了,它們一直處于阻塞狀態,等待新的任務到來,
注意: 本文所說的“核心執行緒”、“非核心執行緒”是一個虛擬的概念,是為了方便描述而虛擬出來的概念,在代碼中并沒有哪個執行緒被標記為“核心執行緒”或“非核心執行緒”,所有執行緒都是一樣的,只是當執行緒池中的執行緒多于指定的核心執行緒數量時,會將多出來的執行緒銷毀掉,池中只保留指定個數的執行緒,那些被銷毀的執行緒是隨機的,可能是第一個創建的執行緒,也可能是最后一個創建的執行緒,或其它時候創建的執行緒,一開始我以為會有一些執行緒被標記為“核心執行緒”,而其它的則是“非核心執行緒”,在銷毀多余執行緒的時候只銷毀那些“非核心執行緒”,而“核心執行緒”不被銷毀,這種理解是錯誤的,
原文鏈接:https://blog.csdn.net/anhenzhufeng/article/details/88870374
著作權宣告:本文為CSDN博主「跑步_跑步」的原創文章,遵循CC 4.0 BY-SA著作權協議,轉載請附上原文出處鏈接及本宣告,
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2021最新版)
2.終于靠開源專案弄到 IntelliJ IDEA 激活碼了,真香!
3.阿里 Mock 工具正式開源,干掉市面上所有 Mock 工具!
4.Spring Cloud 2020.0.0 正式發布,全新顛覆性版本!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/288315.html
標籤:其他
