聲明:此文章是從各個博客整理過來的嗅蔬,如有侵權(quán)請留言谋梭。
Java的Executors類和newCachedThreadPool( )方法类少,根據(jù)API,生成的線程池將重新使用現(xiàn)有Thread對象進(jìn)行新任務(wù)羹唠。
這是如何實(shí)現(xiàn)的奕枢,因?yàn)槲覠o法在API中找到任何方法來設(shè)置現(xiàn)有對象的行為Thread。
例如佩微,可以從一個Runnable對象創(chuàng)建一個新的 Thread缝彬,這使得Thread調(diào)用Runnable的run( )方法。但是哺眯,API中沒有使用setter方法Runnable作為參數(shù)谷浅。
看關(guān)于ThreadPoolExecutor參數(shù)時,看到了keepaliveTime這個參數(shù)奶卓,這個參數(shù)的意思是:“當(dāng)線程數(shù)大于CorePoolSize時一疯,如果有沒有等到新的Task,到了keepaliveTime時間后夺姑,就自動終止掉”墩邀。那么如果在這個時間之前,等到了新的Task盏浙,就可以重用這個線程眉睹。到底是怎么重用線程的呢?
線程重用的核心是只盹,我們知道辣往,Thread.start()只能調(diào)用一次,一旦這個調(diào)用結(jié)束殖卑,則該線程就到了stop狀態(tài)站削,不能再次調(diào)用start。
則要達(dá)到復(fù)用的目的孵稽,則必須從Runnable接口的run()方法上入手许起,可以這樣設(shè)計(jì)這個Runnable.run()方法(就叫外面的run()方法):
它本質(zhì)上是個無限循環(huán),跑的過程中不斷檢查我們是否有新加入的子Runnable對象(就叫內(nèi)部的runnable:run()吧菩鲜,它就是用來實(shí)現(xiàn)我們自己的任務(wù))园细,有就調(diào)一下我們的run(),其實(shí)就一個大run()把其它小run()#1,run()#2,...給串聯(lián)起來了接校,基本原理就這么簡單
不停地處理我們提交的Runnable任務(wù)猛频。
public void run() {
while(true) {
if(tasks available) {
Runnable task = taskqueue.dequeue();
task.run();
} else {
// wait or whatever
}
}
}
jdk節(jié)選
/**
* Main worker run loop. Repeatedly gets tasks from queue and
* executes them, while coping with a number of issues:
*
* 1. We may start out with an initial task, in which case we
* don't need to get the first one. Otherwise, as long as pool is
* running, we get tasks from getTask. If it returns null then the
* worker exits due to changed pool state or configuration
* parameters. Other exits result from exception throws in
* external code, in which case completedAbruptly holds, which
* usually leads processWorkerExit to replace this thread.
*
* 2. Before running any task, the lock is acquired to prevent
* other pool interrupts while the task is executing, and then we
* ensure that unless pool is stopping, this thread does not have
* its interrupt set.
*
* 3. Each task run is preceded by a call to beforeExecute, which
* might throw an exception, in which case we cause thread to die
* (breaking loop with completedAbruptly true) without processing
* the task.
*
* 4. Assuming beforeExecute completes normally, we run the task,
* gathering any of its thrown exceptions to send to afterExecute.
* We separately handle RuntimeException, Error (both of which the
* specs guarantee that we trap) and arbitrary Throwables.
* Because we cannot rethrow Throwables within Runnable.run, we
* wrap them within Errors on the way out (to the thread's
* UncaughtExceptionHandler). Any thrown exception also
* conservatively causes thread to die.
*
* 5. After task.run completes, we call afterExecute, which may
* also throw an exception, which will also cause thread to
* die. According to JLS Sec 14.20, this exception is the one that
* will be in effect even if task.run throws.
*
* The net effect of the exception mechanics is that afterExecute
* and the thread's UncaughtExceptionHandler have as accurate
* information as we can provide about any problems encountered by
* user code.
*
* @param w the worker
*/
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
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();
} 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;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}