由於經常遇到AsyncTask,感覺功能比較簡單,不成想過總結一下,現在感覺自己還是不要太懶,逼自己寫下,總結一下。也可以鍛鍊自己寫博客的能力。
深入理解AsyncTask(一)——線程池
深入理解AsyncTask(一)——AsyncTask
由於AsyncTask是基於線程池基礎實現的,這裏先學習線程池原理。先了解一下線程池的好處:
線程池主要是用在併發操作中,在併發操作中線程比較多,如果每一個線程執行結束了就銷燬,接下來如果新任務來都是時候就需要新建線程,這樣頻繁創建和銷燬線程,會大大降低系統效率。
我們在學習線程池之前,先了解個概念:併發 ,我們也經常聽到並行,兩者有什麼區別呢?
- 併發:同一個處理器在同一時間段處理多個任務,交替執行。
- 並行:不同處理器在同一時刻同時處理不同的任務。
接下來學習線程池的原理
一. ThreadPoolExecutor類
ThreadPoolExecutor是java.uitl.concurrent.ThreadPoolExecutor線程池最核心的一個類,爲了理解透徹這個類,先看下這個類的構造函數
public class ThreadPoolExecutor extends AbstractExecutorService {
.....
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
...
}
可以看到此類繼承於AbstractExecutorService,有四個構造函數,有着分別不同的參數,接下來對其參數分別解釋:
- corePoolSize:核心池的大小,這個參數和後面講述的線程池的實現原理有非常大的關係。在創建線程池後,默認情況下,線程池中並沒有任何線程,而是等待有任務到來的時候才創建線程去執行任務,除非調用prestartAllCoreThreads()或者prestartCoreThread()方法,從這2個方法的名字就可以看出,是預建線程的意思,即在沒有任務到來之前就創建corePoolSize個線程或者一個線程。默認情況下,在創建線程池後,線程池中線程爲0,當有任務來了之後,就會創建一個線程去執行任務,當線程池中的線程數達到corePoolSize,就會把新線程放到緩衝隊列中進行等待。
- maxinumPoolSize:線程池最大的線程數,它表示線程池所能創建的最大數。
- keepAliveTime:表示線程沒有執行任務時存活時間。默認情況下,只有當線程池中線程數大於coolPoolSize纔會觸發keepAliveTime起作用,直到線程池中線程數不大於coolPoolSize。但當調用到allowCoreThreadTimeOut(true)時,此時即使線程池中線程數不大於coolPoolSize,keepAliveTime也會起作用。
- unit:表示keepAliveTime的時間單位,有7中取值,TimeUnit有7種屬性。
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小時
TimeUnit.MINUTES; //分鐘
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //納秒
- workQueue:阻塞隊列,用於緩存等待任務,一般有以下幾種方式:
ArrayBlockingQueue:基於數組的緩衝隊列,需要指定大小;接收到任務的時候,如果沒有達到corePoolSize的值,則新建線程(核心線程)執行任務,如果達到了,
則入隊等候,如果隊列已滿,則新建線程(非核心線程)執行任務,又如果總線程數到了maximumPoolSize,並且隊列也滿了,則發生錯誤
LinkedBlockingQueue:基於鏈表的緩衝隊列,這個隊列接收到任務的時候,如果當前線程數小於核心線程數,則新建線程(核心線程)處理任務;
如果當前線程數等於核心線程數,則進入隊列等待。由於這個隊列沒有最大值限制,即所有超過核心線程數的任務都將被添加到隊列中,這也就導致了maximumPoolSize的設定失效,
因爲總線程數永遠不會超過corePoolSize
SynchronousQueue:這個隊列接收到任務的時候,會直接提交給線程處理,而不保留它,如果所有線程都在工作怎麼辦?
那就新建一個線程來處理這個任務!所以爲了保證不出現<線程數達到了maximumPoolSize而不能新建線程>的錯誤,使用這個類型隊列的時候,
maximumPoolSize一般指定成Integer.MAX_VALUE,即無限大
DelayQueue:隊列內元素必須實現Delayed接口,這就意味着你傳進去的任務必須先實現Delayed接口。這個隊列接收到任務時,首先先入隊,只有達到了指定的延時時間,纔會執行任務
ArrayBlockingQueue和PriorityBlockingQueue使用較少,一般使用LinkedBlockingQueue和Synchronous。線程池的排隊策略與BlockingQueue有關。
- threadFactory:線程工廠,主要用來創建線程;
- handler:表示當拒絕處理任務時的策略,有以下四種取值:
ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
ThreadPoolExecutor與AbstractExecutorService、ExecutorService和Executor之間的簡單繼承關係
ThreadPoolExecutor
AbstractExecutorService
ExecutorService
Executor
- Executor: 是一個頂層接口,在它裏面只聲明瞭一個方法execute(Runnable),返回值爲void,參數爲Runnable類型,從字面意思可以理解,就是用來執行傳進去的任務的;
- ExecutorService:是一個接口,繼承了Executor接口,並聲明瞭一些方法:submit、invokeAll、invokeAny以及shutDown等;
- AbstractExecutorService:是一個抽象類,實現了ExecutorService接口,基本實現了ExecutorService中聲明的所有方法;
- ThreadPoolExecutor:繼承了類AbstractExecutorService。
在ThreadPoolExecutor類中有幾個非常重要的方法:
- execute():實際上是Executor中聲明的方法,在ThreadPoolExecutor進行了具體的實現,這個方法是ThreadPoolExecutor的核心方法,通過這個方法可以向線程池提交一個任務,交由線程池去執行。
- submit():是在ExecutorService中聲明的方法,在AbstractExecutorService就已經有了具體的實現,在ThreadPoolExecutor中並沒有對其進行重寫,這個方法也是用來向線程池提交任務的,但是它和execute()方法不同,它能夠返回任務執行的結果,去看submit()方法的實現,會發現它實際上還是調用的execute()方法,只不過它利用了Future來獲取任務執行結果。
- shutdown():關閉線程池,之前提交的線程繼續執行,不接受新的任務
- shutdownNow():關閉線程池,終止所有任務,已在線程池的線程掛起,並返回線程Runnable列表。
二. 深入剖析線程池實現原理
1. 線程池狀態
在ThreadPoolExecutor中定義了幾個static final變量表示線程池的各個狀態,還通過runState記錄線程池的狀態,runState是存放在變量COUNT_BITS的高位中。先看下這些變量:
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;
當創建線程池時,初始時,線程池的狀態爲RUNNING狀態 如果調用shutdown方法時,此時線程池狀態爲SHUTDOWN狀態 如果調用shutdownNow方法時,此時線程池狀態爲STOP狀態。 如果線程池處於SHUTDOWN或者STOP狀態時,並且所有工作線程都已經銷燬,任務緩衝隊列已經清空或者執行完畢,此時線程池狀態被置爲TERMINATED。
2.任務的執行
在瞭解將任務提交給線程池到任務執行完畢整個過程之前,先看下ThreadPoolExecutor類中重要成員變量:
/**
* The queue used for holding tasks and handing off to worker
* threads. We do not require that workQueue.poll() returning
* null necessarily means that workQueue.isEmpty(), so rely
* solely on isEmpty to see if the queue is empty (which we must
* do for example when deciding whether to transition from
* SHUTDOWN to TIDYING). This accommodates special-purpose
* queues such as DelayQueues for which poll() is allowed to
* return null even if it may later return non-null when delays
* expire.
*/
private final BlockingQueue<Runnable> workQueue; //任務緩存隊列,用來存放等待執行的任務
/**
* Lock held on access to workers set and related bookkeeping.
* While we could use a concurrent set of some sort, it turns out
* to be generally preferable to use a lock. Among the reasons is
* that this serializes interruptIdleWorkers, which avoids
* unnecessary interrupt storms, especially during shutdown.
* Otherwise exiting threads would concurrently interrupt those
* that have not yet interrupted. It also simplifies some of the
* associated statistics bookkeeping of largestPoolSize etc. We
* also hold mainLock on shutdown and shutdownNow, for the sake of
* ensuring workers set is stable while separately checking
* permission to interrupt and actually interrupting.
*/
private final ReentrantLock mainLock = new ReentrantLock();//線程池的主要狀態鎖,對線程池狀態(比如線程池大小
//、runState等)的改變都要使用這個鎖
/**
* Set containing all worker threads in pool. Accessed only when
* holding mainLock.
*/
private final HashSet<Worker> workers = new HashSet<>();//用來存放工作集
/**
* Wait condition to support awaitTermination.
*/
private final Condition termination = mainLock.newCondition();//用來等待的條件變量
/**
* Tracks largest attained pool size. Accessed only under
* mainLock.
*/
private int largestPoolSize; //用來記錄線程池中曾經出現過的最大線程數
/**
* Counter for completed tasks. Updated only on termination of
* worker threads. Accessed only under mainLock.
*/
private long completedTaskCount; //用來記錄已經執行完畢的任務個數
/*
* All user control parameters are declared as volatiles so that
* ongoing actions are based on freshest values, but without need
* for locking, since no internal invariants depend on them
* changing synchronously with respect to other actions.
*/
/**
* Factory for new threads. All threads are created using this
* factory (via method addWorker). All callers must be prepared
* for addWorker to fail, which may reflect a system or user's
* policy limiting the number of threads. Even though it is not
* treated as an error, failure to create threads may result in
* new tasks being rejected or existing ones remaining stuck in
* the queue.
*
* We go further and preserve pool invariants even in the face of
* errors such as OutOfMemoryError, that might be thrown while
* trying to create threads. Such errors are rather common due to
* the need to allocate a native stack in Thread.start, and users
* will want to perform clean pool shutdown to clean up. There
* will likely be enough memory available for the cleanup code to
* complete without encountering yet another OutOfMemoryError.
*/
private volatile ThreadFactory threadFactory; //線程工廠,用來創建線程
/**
* Handler called when saturated or shutdown in execute.
*/
private volatile RejectedExecutionHandler handler; //任務拒絕策略
/**
* Timeout in nanoseconds for idle threads waiting for work.
* Threads use this timeout when there are more than corePoolSize
* present or if allowCoreThreadTimeOut. Otherwise they wait
* forever for new work.
*/
private volatile long keepAliveTime;//線程存貨時間
/**
* If false (default), core threads stay alive even when idle.
* If true, core threads use keepAliveTime to time out waiting
* for work.
*/
private volatile boolean allowCoreThreadTimeOut;//是否允許爲核心線程設置存活時間
/**
* Core pool size is the minimum number of workers to keep alive
* (and not allow to time out etc) unless allowCoreThreadTimeOut
* is set, in which case the minimum is zero.
*/
private volatile int corePoolSize;//核心池的大小(即線程池中的線程數目大於這個參數時,提交的任務會被放進任務緩存隊列)
/**
* Maximum pool size. Note that the actual maximum is internally
* bounded by CAPACITY.
*/
private volatile int maximumPoolSize;//線程池最大能容忍的線程數
/**
* The default rejected execution handler.
*/
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy(); //默認任務拒絕策略,默認爲AbortPolicy
在ThreadPoolExecutor中最核心的提交方法是execute()方法,雖然submit也是提交任務方法,但是submit最終也是調用exceute方法,故這裏只需要重點看execute方法。
/**
* Executes the given task sometime in the future. The task
* may execute in a new thread or in an existing pooled thread.
*
* If the task cannot be submitted for execution, either because this
* executor has been shutdown or because its capacity has been reached,
* the task is handled by the current {@code RejectedExecutionHandler}.
*
* @param command the task to execute
* @throws RejectedExecutionException at discretion of
* {@code RejectedExecutionHandler}, if the task
* cannot be accepted for execution
* @throws NullPointerException if {@code command} is null
*/
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);
}
else if (!addWorker(command, false))
reject(command);
}
2.1. 對execute()方法的實現代碼一句句解析:
先看下具體的實現:
if (command == null) throw new NullPointerException();
上述代碼中可以看到,如果command=null,則直接拋出異常。
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
上述代碼是在判斷工作線程數是否小於corePoolSize後,再調用addWorker()方法將command加入工作線程中去,加入成功直接return。
if (isRunning(c) && workQueue.offer(command))
上述代碼是在工作程線程數大於corePoolSize時,判斷當前線程池狀態是否在RUNNING,此任務可以加入緩存隊列中,接下來需要雙重檢查,防止在加入緩存隊列時,其他線程將線程池關閉,故這裏需要雙重檢查。
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
上述代碼顯示檢查此時線程池狀態是否不處於RUNNING狀態,如果是需要將任務移除隊列,然後使用拒絕策略處理。
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
上述代碼是在雙重判斷之後,雙重判斷之後說明此時線程池還處於RUNNING狀態,此時工作線程數爲0,故需要將任務加入到工作線程中。
else if (!addWorker(command, false))
reject(command);
上述代碼是在第一次線程池狀態判斷時,此時線程池不處於RUNNING狀態或者緩存隊列沒有找到該任務,此時會將任務加入到工作線程中,如果加入失敗,此時會調用拒絕策略進行處理。
2.2. 接下來我們看下addWorker方法:
看下函數體的實現:
/*
* Methods for creating, running and cleaning up after workers
*/
/**
* Checks if a new worker can be added with respect to current
* pool state and the given bound (either core or maximum). If so,
* the worker count is adjusted accordingly, and, if possible, a
* new worker is created and started, running firstTask as its
* first task. This method returns false if the pool is stopped or
* eligible to shut down. It also returns false if the thread
* factory fails to create a thread when asked. If the thread
* creation fails, either due to the thread factory returning
* null, or due to an exception (typically OutOfMemoryError in
* Thread.start()), we roll back cleanly.
*
* @param firstTask the task the new thread should run first (or
* null if none). Workers are created with an initial first task
* (in method execute()) to bypass queuing when there are fewer
* than corePoolSize threads (in which case we always start one),
* or when the queue is full (in which case we must bypass queue).
* Initially idle threads are usually created via
* prestartCoreThread or to replace other dying workers.
*
* @param core if true use corePoolSize as bound, else
* maximumPoolSize. (A boolean indicator is used here rather than a
* value to ensure reads of fresh values after checking other pool
* state).
* @return true if successful
*/
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);
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();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
可以看到此方法的兩個參數:
- firstTask:任務
- core:表示是否是核心線程
2.3. 對addWorker()方法的實現一句句進行分析:
先看下這個for循環,在for表達式中是沒有條件停止,說明循環體中可以將這個循環停止。
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
}
}
看下循環體中的內容:
- 獲取線程池的狀態,也就是rs,然後對rs進行判斷,可以看到當rs >= SHUTDOWN也就是線程池此時不處於RUNNING狀態,或者firstTask==null時,此語句的判斷的目的就是看線程池是否在運行,還有任務是否空,如果是,則返回false;
- 接下來是第二個for循環,for表達式中也是沒有條件的,可以看下其循環體內容:
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
上述代碼中先獲取工作線程數wc,然後判斷工作線程數是否大於corePoolSize或者maximumPoolSize;這個很好理解,如果core=true,表示此時將任務加入到核心工作線程中,此時wc需要和corePoolSize比較,否則需要和maximumPoolSize比較。 這裏的CAPACITY,字面意思理解是容量,我個人理解應該是系統的最大容量。
if (compareAndIncrementWorkerCount(c))
break retry;
上述代碼中c是一個原子性的變量,通過此變量可以獲取線程池狀態和工作線程數,這裏調用compareAndIncrementWorkerCount()方法是嘗試增加原子變量c的值。如果增加失敗,則跳出標記語句。
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
上述代碼重新獲取c的值,通過c的值獲取到線程池的狀態,此時又一次判斷線程池的狀態,防止其他線程將線程池關閉。如果狀態不一致,進行下次循環。
- 接下來真正的將任務加入工作線程中:
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
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();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
可以看出上述代碼中先新建一個Worker對象w,然後判斷線程池在RUNNING狀態時,調用workers.add(w),此時改變狀態worker=ture和workerStarted=true。然後在finally方法中判斷是否add失敗,如果失敗,就會調用addWorkerFailed()方法,這裏就這個方法就不做重點介紹了。
2.4. 接下來我們看下Worker對象的實現:
/**
* Class Worker mainly maintains interrupt control state for
* threads running tasks, along with other minor bookkeeping.
* This class opportunistically extends AbstractQueuedSynchronizer
* to simplify acquiring and releasing a lock surrounding each
* task execution. This protects against interrupts that are
* intended to wake up a worker thread waiting for a task from
* instead interrupting a task being run. We implement a simple
* non-reentrant mutual exclusion lock rather than use
* ReentrantLock because we do not want worker tasks to be able to
* reacquire the lock when they invoke pool control methods like
* setCorePoolSize. Additionally, to suppress interrupts until
* the thread actually starts running tasks, we initialize lock
* state to a negative value, and clear it upon start (in
* runWorker).
*/
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接口。這裏最重要的方式是run()
a. 先看下構造函數:
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
這裏getThreadFactory().newThread(this)作用有點像new Thread(this),根據Java的多態性質,此時this作爲一個Runnable對象傳入。
b. 再看下run()方法的實現:
/** Delegates main run loop to outer runWorker. */
public void run() {
runWorker(this);
}
c. run()調用runWorker()方法:
/**
* 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);
}
}
上述代碼中最關鍵的一句就是task.run(),這個task是通過getTask()方法拿到,這裏其實緩存隊列中的任務,也就是最終調用任務自己的run()方法。任務自己的run()需要任務本身實現的,這裏不做太多講解。這裏比較關心getTask()方法。看下其實現:
/**
* Performs blocking or timed wait for a task, depending on
* current configuration settings, or returns null if this worker
* must exit because of any of:
* 1. There are more than maximumPoolSize workers (due to
* a call to setMaximumPoolSize).
* 2. The pool is stopped.
* 3. The pool is shutdown and the queue is empty.
* 4. This worker timed out waiting for a task, and timed-out
* workers are subject to termination (that is,
* {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
* both before and after the timed wait, and if the queue is
* non-empty, this worker is not the last thread in the pool.
*
* @return task, or null if the worker must exit, in which case
* workerCount is decremented
*/
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);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
- 先獲取線程池狀態,如果線程池狀態不處於RUNNING,而且緩存隊列爲空,此時就減少工作線程。
- 接下來會去獲取任務數,如果此任務數大於最大任務數時或者已經超時時或者緩存隊列空時,此時會去嘗試增加工作線程數,如果失敗則返回,繼續循環。
- 接下來緩存隊列會poll出一個Worker對象處理進行處理
總結:
- 如果當線程池中的線程數小於corePoolSize,則每來一個任務,就會創建一個線程去執行這個任務;
- 如果當線程中的線程數>=corePoolSize,每來一個任務,會嘗試將其添加到任務緩存隊列中,若添加成功,該任務會等待空閒線程將其取出執行,若添加失敗(一般來說是任務緩存隊列已滿),則會嘗試創建新的線程去執行這個任務;
- 如果當前線程池中的線程數目達到maximumPoolSize,則會採取任務拒絕策略進行處理;
- 如果線程池中的線程數量大於 corePoolSize時,如果某線程空閒時間超過keepAliveTime,線程將被終止,直至線程池中的線程數目不大於corePoolSize;如果允許爲核心池中的線程設置存活時間,那麼核心池中的線程空閒時間超過keepAliveTime,線程也會被終止。
3.線程池中的線程初始化
默認情況下,創建線程池之後,線程池中是沒有線程的,需要提交任務之後纔會創建線程。
在實際中如果需要線程池創建之後立即創建線程,可以通過以下兩個方法辦到:
- prestartCoreThread():初始化一個核心線程;
- prestartAllCoreThreads():初始化所有核心線程
下面是這2個方法的實現:
public boolean prestartCoreThread() {
return addIfUnderCorePoolSize(null); //注意傳進去的參數是null
}
public int prestartAllCoreThreads() {
int n = 0;
while (addIfUnderCorePoolSize(null))//注意傳進去的參數是null
++n;
return n;
}
注意: 上面傳進去的參數是null,根據第2小節的分析可知如果傳進去的參數爲null,則最後執行線程會阻塞在getTask方法中的
r = workQueue.take();
即等待任務隊列中有任務。
4.任務緩存隊列及排隊策略
在前面我們多次提到了任務緩存隊列,即workQueue,它用來存放等待執行的任務。 workQueue的類型爲BlockingQueue<Runnable>,通常可以取下面三種類型:
- ArrayBlockingQueue:基於數組的先進先出隊列,此隊列創建時必須指定大小;
- LinkedBlockingQueue:基於鏈表的先進先出隊列,如果創建時沒有指定此隊列大小,則默認爲Integer.MAX_VALUE;
- synchronousQueue:這個隊列比較特殊,它不會保存提交的任務,而是將直接新建一個線程來執行新來的任務。
5.任務拒絕策略
當線程池的任務緩存隊列已滿並且線程池中的線程數目達到maximumPoolSize,如果還有任務到來就會採取任務拒絕策略,通常有以下四種策略:
- ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。
- ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。
- ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
- ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
6.線程池的關閉
ThreadPoolExecutor提供了兩個方法,用於線程池的關閉,分別是shutdown()和shutdownNow(),其中:
- shutdown():不會立即終止線程池,而是要等所有任務緩存隊列中的任務都執行完後才終止,但再也不會接受新的任務
- shutdownNow():立即終止線程池,並嘗試打斷正在執行的任務,並且清空任務緩存隊列,返回尚未執行的任務
7.線程池容量的動態調整
ThreadPoolExecutor提供了動態調整線程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),
- setCorePoolSize:設置核心池大小
- setMaximumPoolSize:設置線程池最大能創建的線程數目大小
當上述參數從小變大時,ThreadPoolExecutor進行線程賦值,還可能立即創建新的線程來執行任務。
三. 常見四種線程池
Java通過Executors提供了四種線程池,這四種線程池都是直接或間接配置ThreadPoolExecutor的參數實現的,下面我都會貼出這四種線程池構造函數的源碼,可以看出其構成。
1. CachedThreadPool
字面理解是可緩存線程池,其實就是這個意思。
看下它的優點:
- 線程數無限制
- 有空閒線程則複用空閒線程,若無空閒線程則新建線程
- 一定程度減少頻繁創建/銷燬線程,減少系統開銷
創建方法:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
源碼:
/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available, and uses the provided
* ThreadFactory to create new threads when needed.
* @param threadFactory the factory to use when creating new threads
* @return the newly created thread pool
* @throws NullPointerException if threadFactory is null
*/
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
可以看出corePoolSize = 0;maximumPoolSize = Integer.MAX_VALUE; 超時時間爲60s,使用的是SynchronousQueue類型隊列
2.FixedThreadPool
字面理解是固定長度線程池
看下優點:
- 可控制線程最大併發數(同時執行的線程數)
- 超出的線程會在隊列中等待
創建方法:
//nThreads => 最大線程數即maximumPoolSize
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads);
//threadFactory => 線程工廠類,用於創建線程
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory);
源碼:
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue, using the provided
* ThreadFactory to create new threads when needed. At any point,
* at most {@code nThreads} threads will be active processing
* tasks. If additional tasks are submitted when all threads are
* active, they will wait in the queue until a thread is
* available. If any thread terminates due to a failure during
* execution prior to shutdown, a new one will take its place if
* needed to execute subsequent tasks. The threads in the pool will
* exist until it is explicitly {@link ExecutorService#shutdown
* shutdown}.
*
* @param nThreads the number of threads in the pool
* @param threadFactory the factory to use when creating new threads
* @return the newly created thread pool
* @throws NullPointerException if threadFactory is null
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
兩種方法唯一不同就是在線程工廠上,這裏看下共同之處:corePoolSize = nThreads;maximumPoolSize = InThreads; 超時時間爲0ms,使用的是LinkedBlockingQueue類型隊列
3.ScheduledThreadPool
字面理解爲計劃線程池
看下優點:
- 支持定時及週期性任務執行
創建方法:
//nThreads => 最大線程數即maximumPoolSize
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);
//threadFactory => 線程工廠類,用於創建線程
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(int nThreads, ThreadFactory threadFactory);
源碼:
/**
* Creates a thread pool that can schedule commands to run after a
* given delay, or to execute periodically.
* @param corePoolSize the number of threads to keep in the pool,
* even if they are idle
* @return a newly created scheduled thread pool
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
/**
* Creates a thread pool that can schedule commands to run after a
* given delay, or to execute periodically.
* @param corePoolSize the number of threads to keep in the pool,
* even if they are idle
* @param threadFactory the factory to use when the executor
* creates a new thread
* @return a newly created scheduled thread pool
* @throws IllegalArgumentException if {@code corePoolSize < 0}
* @throws NullPointerException if threadFactory is null
*/
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
追蹤其源碼會調用到這:
/**
* Creates a new {@code ScheduledThreadPoolExecutor} 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 threadFactory the factory to use when the executor
* creates a new thread
* @throws IllegalArgumentException if {@code corePoolSize < 0}
* @throws NullPointerException if {@code threadFactory} is null
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory);
}
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default rejected execution handler.
*
* @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
* @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} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
可以看到corePoolSize = corePoolSize; maximumPoolSize = Integer.MAX_VALUE; 超時時間爲DEFAULT_KEEPALIVE_MILLIS毫秒(10ms),使用的是DelayedWorkQueue類型隊列
4.SingleThreadExecutor
字面理解爲單線程線程池
看下其優點:
- 有且僅有一個工作線程執行任務
- 所有任務按照指定順序執行,即遵循隊列的入隊出隊規則
創建方法:
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
源碼:
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newFixedThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue, and uses the provided ThreadFactory to
* create a new thread when needed. Unlike the otherwise
* equivalent {@code newFixedThreadPool(1, threadFactory)} the
* returned executor is guaranteed not to be reconfigurable to use
* additional threads.
*
* @param threadFactory the factory to use when creating new
* threads
*
* @return the newly created single-threaded Executor
* @throws NullPointerException if threadFactory is null
*/
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
兩個方法可以看出corePoolSize = 1; maximumPoolSize = 1; 超時時間爲0ms,使用的是LinkedBlockingQueue類型隊列
結語
寫這篇文章掙扎了好久,因爲這塊的知識其實已經被別人已經梳理的差不多了,而且自己對這塊也很熟悉了。後來還是決定動手寫下吧,防止以後忘記,也可以將知識傳播給大家,這裏借鑑了網上大神的分析方法,內容絕對原創分析,基於自己的內容,最新的代碼進行的分析,小弟剛剛寫起博客沒有多久,大家如果發現問題可以一起討論,下篇繼續介紹AsyncTask,謝謝。