About ExecutorService(1),F(xiàn)uture&FutureTask
About ExecutorService(2),自定義線(xiàn)程池
About ExecutorService(3),我所認(rèn)識(shí)的AsyncTask
About ExecutorService(4),AsyncTask番外篇
這些小知識(shí)點(diǎn)作為AT的番外篇再適合不過(guò)了。
直接切入正題,我們都知道AT在3.X將默認(rèn)的線(xiàn)程池由并行改為串行,真的是眾所周知的3.0(HONEYCOMB)開(kāi)始的嘛?答案是否定的。確切的說(shuō)是從3.2(HONEYCOMB_MR1)起。
我們拿Android-22舉個(gè)例子,根據(jù)路徑打開(kāi),<Sdk根目錄>\sources\android-22\android\app\ActivityThread.java,找到這樣的一段代碼(源碼沒(méi)有那段中文注釋)
// If the app is Honeycomb MR1 or earlier, switch its AsyncTask
// implementation to use the pool executor. Normally, we use the
// serialized executor as the default. This has to happen in the
// main thread so the main looper is set right .
if (data.appInfo.targetSdkVersion <= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) {
/*設(shè)置默認(rèn)線(xiàn)程池*/
AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
接下來(lái)我們只需要對(duì)照android.os.Build.VERSION_CODES,找到HONEYCOMB_MR1代表哪個(gè)版本就OK了。

由此便可得出結(jié)論,3.2及其之后,AT的線(xiàn)程池使用串行代替并行。
但是......
前兩天做消息推送的時(shí)候需要使用一些Android的Compat(兼容)包或類(lèi)。于是今天好奇的想看看AT有沒(méi)有兼容包或者類(lèi),于是就發(fā)現(xiàn)了這個(gè)AsyncTaskCompat類(lèi),這個(gè)類(lèi)的主要功能就是提供一個(gè)并行線(xiàn)程池的AT(Parallel:并行)。位于<SDK根目錄>\extras\android\m2repository\com\android\support\support-v4\22.2.0\support-v4-22.2.0-sources.jar!\android\support\v4\os\AsyncTaskCompat.java
代碼很簡(jiǎn)短,如下:
/**
* Helper for accessing features in {@link android.os.AsyncTask}
* introduced after API level 4 in a backwards compatible fashion.
*/
public class AsyncTaskCompat {
/**
* Executes the task with the specified parameters, allowing multiple tasks to run in parallel
* on a pool of threads managed by {@link android.os.AsyncTask}.
*
* @param task The {@link android.os.AsyncTask} to execute.
* @param params The parameters of the task.
* @return the instance of AsyncTask.
*/
public static <Params, Progress, Result> AsyncTask<Params, Progress, Result> executeParallel(
AsyncTask<Params, Progress, Result> task,
Params... params) {
if (task == null) {
throw new IllegalArgumentException("task can not be null");
}
if (Build.VERSION.SDK_INT >= 11) {
// From API 11 onwards, we need to manually select the THREAD_POOL_EXECUTOR
AsyncTaskCompatHoneycomb.executeParallel(task, params);
} else {
// Before API 11, all tasks were run in parallel
task.execute(params);
}
return task;
}
}
接下來(lái):

這段代碼的注釋與ActivityThread中代碼的注釋有沖突,之前明確指出只有13及其之后才會(huì)使用串行線(xiàn)程池代替并行。個(gè)人觀點(diǎn)更傾向于這樣,顯得規(guī)范一些:
if (Build.VERSION.SDK_INT >= 13) {
// From API 13 onwards, we need to manually select the THREAD_POOL_EXECUTOR
AsyncTaskCompatHoneycomb.executeParallel(task, params);
} else {
// Before API 13, all tasks were run in parallel
task.execute(params);
}
雖然之前的寫(xiě)法并沒(méi)有錯(cuò),但很容易給開(kāi)發(fā)者帶來(lái)困惑。
既然這里又提到了并發(fā),就不得不提一下有關(guān)“鎖”的優(yōu)化,確切的說(shuō)是AT中“鎖”的優(yōu)化。
多核時(shí)代的來(lái)臨,使用多線(xiàn)程可以顯著提高系統(tǒng)的性能,但是,單線(xiàn)程真的“一無(wú)是處”了嗎,答案依然是否定的,對(duì)于那些單線(xiàn)程或者單任務(wù)的程序來(lái)說(shuō),主要資源都消耗在任務(wù)本身,既不需要維護(hù)并行數(shù)據(jù)結(jié)構(gòu)間的一致性狀態(tài),也不需要為線(xiàn)程的切換和調(diào)度花費(fèi)不必要的時(shí)間,而且,對(duì)于多線(xiàn)程而言,系統(tǒng)除了處理任務(wù)外,還需要維護(hù)多線(xiàn)程環(huán)境的特有信息,比如:線(xiàn)程本身的數(shù)據(jù)元,線(xiàn)程調(diào)度,甚至是線(xiàn)程上下文的切換等。
事實(shí)上,在單核CPU(然而單核mobile并沒(méi)有什么卵用)上,采用并行算法的效率一般要低于原始的串行算法。因此,并行計(jì)算之所以能夠提高系統(tǒng)的性能,并不是因?yàn)榫€(xiàn)程偷懶,少干活,而是因?yàn)椴⑿杏?jì)算可以更合理的進(jìn)行任務(wù)調(diào)度。所以,合理的并發(fā),才能將多核CPU(真·八核?!)的性能發(fā)揮效果。
然而,在多核,多線(xiàn)程,多任務(wù)時(shí)代,為了保證數(shù)據(jù)的同步,“鎖”扮演著不可或缺的角色。優(yōu)雅的異步,并行,并發(fā)等算法結(jié)構(gòu),都離不開(kāi)“鎖”,然而,激烈的鎖競(jìng)爭(zhēng)又會(huì)導(dǎo)致程序性能的下降。
因此,在使用“鎖”的時(shí)候,我們也應(yīng)該盡量考慮以下幾點(diǎn):
-
減少鎖持有時(shí)間
只在必要時(shí)進(jìn)行同步,這樣就能明顯減少線(xiàn)程持有鎖的時(shí)間,提高系統(tǒng)的吞吐量
-
減小鎖粒度
縮小鎖定對(duì)象的范圍,從而減少鎖沖突的可能性,進(jìn)而提高系統(tǒng)的并發(fā)能力,例如ConcurrentHashMap的桶鎖機(jī)制。
-
重用鎖(
ReentrantLock)代替內(nèi)部鎖(synchronized)
重用鎖提供了很多內(nèi)部鎖不具備的強(qiáng)大功能,比如鎖等待時(shí)間( boolean tryLock(long timeout, TimeUnit unit))、支持中斷鎖(void lockInterruptibly( ))等,這些API有助于避免死鎖,提高系統(tǒng)穩(wěn)定性,使用ReentrantLock一定要注意在finally中釋放。
-
粗化鎖
這個(gè)解釋起來(lái)比較籠統(tǒng),與減少鎖持有時(shí)間在含義上是相反的。因?yàn)殒i的競(jìng)爭(zhēng)與釋放,也是需要消耗資源的,因此當(dāng)我們需要在循環(huán)內(nèi)請(qǐng)求鎖時(shí),
需要寫(xiě)成這樣:
synchronized (this) {
for (int i = 0; i < count; i++) {
/*do something*/
}
}
而不是這樣:
for (int i = 0; i < count; i++) {
synchronized (this) {
/*do something*/
}
}
接下來(lái)看一段AT中的源碼:

使用線(xiàn)程安全的雙端隊(duì)列LinkedBlockingDeque代替ArrayDeque,從而可以減少鎖持有時(shí)間,使用重用鎖(ReentrantLock)代替內(nèi)部鎖(synchronized)進(jìn)行雙重校驗(yàn),避免高并發(fā)狀態(tài)下scheduleNext方法不必要的鎖等待。
private static class SerialExecutor implements Executor {
final LinkedBlockingDeque<Runnable> mTasks = new LinkedBlockingDeque<>();
Runnable mActive;
final ReentrantLock reentrantLock = new ReentrantLock();
public void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
SerialExecutor.this.scheduleNext();
}
}
});
if (mActive == null) {
reentrantLock.lock();
try {
if (mActive == null) {
SerialExecutor.this.scheduleNext();
}
} finally {
reentrantLock.unlock();
}
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
差不多這就是這樣了,如果有理解不當(dāng),或邏輯錯(cuò)誤,還望指出。
片尾TIP:
一張圖足夠說(shuō)明了。
