線程和線程池

線程

在java中實現(xiàn)線程的方式:

  1. 繼承Thread類
  2. 實現(xiàn)Runable接口。

main方法其實也是一個線程。
在java中,每次程序運行至少啟動2個線程。一個是main線程,一個是垃圾收集線程。

  • 對比
    實現(xiàn)Runnable接口比繼承Thread類所具有的優(yōu)勢:
    1):適合多個相同的程序代碼的線程去處理同一個資源
    2):可以避免java中的單繼承的限制
    3):增加程序的健壯性,代碼可以被多個線程共享,代碼和數(shù)據(jù)獨立

  • yield()方法

    Thread.yield()方法作用是:暫停當前正在執(zhí)行的線程對象,并執(zhí)行其他線程。
    yield()應該做的是讓當前運行線程回到可運行狀態(tài),以允許具有相同優(yōu)先級的其他線程獲得運行機會。因此,使用yield()的目的是讓相同優(yōu)先級的線程之間能適當?shù)妮嗈D執(zhí)行。但是,實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被線程調度程序再次選中。

結論:yield()從未導致線程轉到等待/睡眠/阻塞狀態(tài)。在大多數(shù)情況下,yield()將導致線程從運行狀態(tài)轉到可運行狀態(tài),但有可能沒有效果。

  • join()方法
    保證當前線程停止執(zhí)行,直到該線程所加入的線程完成為止,當前線程方可繼續(xù)執(zhí)行。然而,如果它加入的線程沒有存活,則當前線程不需要停止。

AsyncTask

  • 內部由兩個線程池和一個Handler組成。
    1. SerialExecutor:用于任務的排隊,一次執(zhí)行一個。
    2. ThreadPoolExecutor:用于真正的執(zhí)行任務。
    3. InternalHandler:用于發(fā)送結果數(shù)據(jù)從子線程到主線程。
  • 執(zhí)行流程:
    構造方法中實例化WorkerRunnable和FutureTask對象。
    WorkerRunnable將Params參數(shù)封裝,并將自己封裝在FutureTask中,一旦FutureTask執(zhí)行run方法時,會去調用WorkRunnable的call方法并返回Result值。call方法中就執(zhí)行了AsyncTask的doInBackground方法。并調用postResult方法將結果發(fā)送出去。
mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);

        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        //noinspection unchecked
        Result result = doInBackground(mParams);
        Binder.flushPendingCommands();
        return postResult(result);
    }
};

  private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
  }

那么FutureTask是什么時候執(zhí)行的?
FutureTask充當了Runnable的作用,交給SerialExecutor的execute方法執(zhí)行。FutureTask是一個并發(fā)類,可以中途取消的用于異步計算的類。

SerialExecutor的execute方法首先把FutureTask插入到mTasks任務隊列中,如果沒有活動的任務,則執(zhí)行下一個。當一個任務執(zhí)行完成,會繼續(xù)調用scheduleNext方法執(zhí)行下一個,直到所有任務都被執(zhí)行。

THREAD_POOL_EXECUTOR.execute(mActive);才是真正執(zhí)行任務的方法。使用的是ThreadPoolExecutor線程池。

private static class SerialExecutor implements Executor {
  final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
  Runnable mActive;

  public synchronized void execute(final Runnable r) {
      //將FutureTask插入到mTasks任務隊列中
      mTasks.offer(new Runnable() {
          public void run() {
              try {
                  // 執(zhí)行FutureTask的run方法,進而執(zhí)行call方法
                  r.run();
              } finally {
                  // 串行執(zhí)行下一個任務
                  scheduleNext();
              }
          }
      });
      //沒有正在活動的任務,執(zhí)行下一個AsyncTask。
      if (mActive == null) {
          scheduleNext();
      }
  }

  protected synchronized void scheduleNext() {
      if ((mActive = mTasks.poll()) != null) {
          THREAD_POOL_EXECUTOR.execute(mActive);
      }
  }
}
  • 注意:
  1. AsyncTask對象必須在主線程中創(chuàng)建
  2. execute必須在主線程中調用
  3. 一個AsyncTask只能調用一次execute方法,

HandlerThread

繼承了Thread類,本質還是線程。但是可以直接使用Handler的Thread。在run方法中通過Looper.prepare創(chuàng)建消息隊列,并開啟消息循環(huán)。使得可以再此線程中創(chuàng)建Handler。

由于loop開啟了無限循環(huán),因此可以通過quit或者quitSafely方法終止線程執(zhí)行。

同時,它還解決了一個Looper與Handler的同步問題??梢员WC根據(jù)當前線程的Looper創(chuàng)建Handler時,Looper對象的獲取不為空。
參考《深入理解Android 卷I》159頁

    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

典型應用場景就是在IntentService中。

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

IntentService

一個可以處理異步請求的Service.服務開啟后,工作線程會依次處理每個Intent,任務執(zhí)行完畢后會自動關閉。

相對于線程而言,IntentService更適合執(zhí)行一些高優(yōu)先級的后臺任務。

    @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

線程池

  • 優(yōu)點
  1. 重用線程池中的線程,避免因為線程的創(chuàng)建和銷毀帶來的性能開銷。
  2. 有效控制線程的最大并發(fā)數(shù),避免因大量的線程之間互相搶占系統(tǒng)資源而導致的阻塞 。
  3. 能夠對線程進行簡單的管理,并提供定時執(zhí)行和執(zhí)行循環(huán)間隔執(zhí)行等功能
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {  }
  • 變量

    • corePoolSize: 核心線程數(shù)
    • maximumPoolSize: 最大線程數(shù)
    • workQueue:任務隊列,提交的Runnable對象存儲在這里。
    • keepAliveTime: 非核心線程存活時間
    • unit:keepAliveTime的時間單位。
    • threadFactory:為線程池提供新線程的工廠。
    • handler:異常處理策略。
  • 規(guī)則

  1. 如果此時線程池中的數(shù)量小于corePoolSize,即使線程池中的線程都處于空閑狀態(tài),也要創(chuàng)建新的線程來處理被添加的任務。
  2. 如果此時線程池中的數(shù)量等于 corePoolSize,但是緩沖隊列 workQueue未滿,那么任務被放入緩沖隊列。
  3. 如果此時線程池中的數(shù)量大于corePoolSize,緩沖隊列workQueue滿,并且線程池中的數(shù)量小于maximumPoolSize,建新的線程來處理被添加的任務。
  4. 如果此時線程池中的數(shù)量大于corePoolSize,緩沖隊列workQueue滿,并且線程池中的數(shù)量等于maximumPoolSize,那么通過 handler所指定的策略來處理此任務。

參閱:
Java多線程學習

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

相關閱讀更多精彩內容

  • Android中的線程 線程,在Android中是非常重要的,主線程處理UI界面,子線程處理耗時操作。如果在主線程...
    shenhuniurou閱讀 876評論 0 3
  • 從用途上來說,線程分為主線程和子線程,主線程主要處理和界面相關的事情,子線程則往往用于執(zhí)行耗時操作。 除了Thre...
    小柏不是大白閱讀 709評論 0 3
  • 線程 操作系統(tǒng)調度最小單元,不可能無限制產生(受限的系統(tǒng)資源),線程創(chuàng)建與銷毀有相應的開銷。當系統(tǒng)中存在大量線程的...
    墨染書閱讀 677評論 1 5
  • 11.1 主線程和子線程 Android中的主線程主要處理和界面相關的事情,而子線程則往往用于執(zhí)行耗時操作,如網絡...
    Xerrard閱讀 434評論 0 0
  • 一張稚嫩的臉龐 變得越發(fā)成熟 曾經的懵懂無知 今朝回憶 笑顏綻放 兒時的兩小無猜 青春的互生愛戀 好奇變成探索 快...
    未來之光閱讀 171評論 0 0

友情鏈接更多精彩內容