About ExecutorService(4),AsyncTask番外篇

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了。

蜂巢3.1

由此便可得出結(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):


`AsyncTaskCompat`中部分源碼截圖

這段代碼的注釋與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中的源碼:

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ō)明了。

Android studio生成style和include
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容