Android的線程和線程池--《Android開發藝術探索》閱讀筆記——第十一章

除了Thread,Android中扮演線程的角色還有:AsyncTask、HandlerThread、IntentService。

  • AsyncTask:內部封裝線程池、handler,便於在子線程中更新UI。
  • HandlerThread:可以使用消息循環的線程,在它內部可以使用Handler。
  • IntentService:內部使用HandlerThread執行任務,完畢後會自動退出。(相比後臺線程)因是組件,優先級高,不易被殺死。

線程是操作系統調度的最小單元,是一種受限的資源,不可能無限制的產生。且線程的創建和銷燬需要相應的開銷。且存在大量線程時,系統會通過時間片輪轉的方式調度線程,因此線程不可能做到並行,除非線程數小於等於cpu數。所以需要 線程池,它可以緩存一定數量的線程,避免頻繁地線程創建和銷燬帶來的系統開銷。

一、Android中的線程形態

1.1 AsyncTask

AsyncTask是用來在線程池中處理異步任務,並可以把處理進度和結果發送到UI線程。

1.1.1 使用方法

AsyncTask的基本使用方法,示例如下:

    private void testAsyncTask() {
        //一般要在主線程實例化。(實際在9.0上 子線程創建實例然後主線程execute沒問題)
        //三個泛型參數依次表示參數類型、進度類型、結果類型。
        //覆寫的這幾個方法不可以直接調用
        AsyncTask<Integer, Integer, String> task = new AsyncTask<Integer, Integer, String>() {

            @Override
            protected void onPreExecute() {
                super.onPreExecute();
                //主線程執行,在異步任務之前
                Log.i(TAG, "testAsyncTask onPreExecute: ");
            }

            @Override
            protected String doInBackground(Integer... integers) {
                Log.i(TAG, "testAsyncTask doInBackground: ");
                //任務在 線程池中執行 耗時操作
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //發出進度
                publishProgress(50);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //再發出進度
                publishProgress(100);

                return "我是結果。參數是" + integers[0];
            }

            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);
                //在主線程執行,在異步任務執行完之後
                Log.i(TAG, "testAsyncTask onPostExecute: "+s);
            }

            @Override
            protected void onProgressUpdate(Integer... values) {
                super.onProgressUpdate(values);
                //執行在主線程,調用publishProgress()後就會執行
                Log.i(TAG, "testAsyncTask onProgressUpdate: 進度:"+values[0]+"%");
            }

            @Override
            protected void onCancelled() {
                super.onCancelled();
                //取消任務
            }
        };
        //必須要在主線程執行execute,且只能執行一次
        task.execute(100);
    }

執行結果日誌如下:

2020-01-14 11:29:03.510 13209-13209/com.hfy.demo01 I/hfy: testAsyncTask onPreExecute: 
2020-01-14 11:29:03.511 13209-13282/com.hfy.demo01 I/hfy: testAsyncTask doInBackground: 
2020-01-14 11:29:04.558 13209-13209/com.hfy.demo01 I/hfy: testAsyncTask onProgressUpdate: 進度:50%
2020-01-14 11:29:05.589 13209-13209/com.hfy.demo01 I/hfy: testAsyncTask onProgressUpdate: 進度:100%
2020-01-14 11:29:05.590 13209-13209/com.hfy.demo01 I/hfy: testAsyncTask onPostExecute: 我是結果。參數是100

1.1.2 原理分析:

先看構造方法

    public AsyncTask() {
        this((Looper) null);
    }
    public AsyncTask(@Nullable Handler handler) {
        this(handler != null ? handler.getLooper() : null);
    }

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                	//執行完,發出結果
                    postResult(result);
                }
                return result;
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }
    private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
            	//這裏傳入的是主線程的looper,所以用來把消息切到主線程
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

看到,首先使用主線程的Looper創建了InternalHandler實例。然後創建了WorkerRunnable的實例mWorker,call方法中看到調用了 doInBackground(mParams),可以猜想call方法是執行在線程池的。然後創建了FutureTask的實例mFuture並傳入了mWorker,mFuture怎麼使用的呢?後面會分析道。我們可以先看下Handler的實現InternalHandler:

    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

看到有處理髮送結果、處理髮送進度的消息。消息從哪發來的呢?先留個疑問。繼續看AsyncTask的execute方法:

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    /**
     *  串行 執行器
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    
    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }

        mStatus = Status.RUNNING;

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

execute方法走到了executeOnExecutor方法,先進行當前任務狀態的判斷,默認是準備執行任務的PENDING狀態,然後變爲RUNNING。但如果正在執行的RUNNING、執行完的FINISHED都會拋出異常。這也是一個任務實例只能執行一次的原因。然後又走到了onPreExecute(),因爲execute執行在UI線程 所以也解釋了其是執行在UI線程的原因。接着把參數賦值給mWorker,mFuture作爲參數執行 靜態的sDefaultExecutor的execute()方法。注意到sDefaultExecutor是SerialExecutor實例,去瞅瞅:

	//線程池
    public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }
    
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
		//execute方法加了鎖
        public synchronized void execute(final Runnable r) {
        	//把r存入到任務隊列的隊尾
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                    	//任務執行完,就執行下一個
                        scheduleNext();
                    }
                }
            });
            //把r存入任務隊列後,然後當前沒有取出的任務,就 取 隊列頭部 的任務執行
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
        	//取 隊列頭部 的任務執行
            if ((mActive = mTasks.poll()) != null) {
            	//THREAD_POOL_EXECUTOR是線程池
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

上面都有註釋,可見SerialExecutor就是串行執行器,最終執行在THREAD_POOL_EXECUTOR的線程池中。r.run()實際走的是FutureTask的run方法:

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
    
    public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                
                	//callable的call方法
                    result = c.call();
                    
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

FutureTask的run方法中調用的傳入的callable的call()方法,再結合上面AsyncTask的構造方法,mWorker就是實現callable的call()方法。所以裏面的doInBackground方法就會串行的執行在線程池中。因爲串行,那使用execute方法不能執行特別耗時的任務,否則會阻塞後面等待的任務。若想要並行,可採用AsyncTask的executeOnExecutor方法,傳入線程池THREAD_POOL_EXECUTOR即可

還注意到,doInBackground執行完後調用了postResult(result),result就是doInBackground返回值:

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

看到,使用handler發送消息,消息類型就是MESSAGE_POST_RESULT,前面看到InternalHandler內部handleMessage是有處理的,就是調用task的finish方法:

    private void finish(Result result) {
        if (isCancelled()) {
        	//如果任務取消了,回調onCancelled
            onCancelled(result);
        } else {
        	//沒有取消
            onPostExecute(result);
        }
        //修改任務狀態爲完成
        mStatus = Status.FINISHED;
    }

可見如果沒有調用cancel(),就會走onPostExecute,所以onPostExecute也是執行在UI線程的

最後看下publishProgress方法:

    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

如果沒有取消任務,也是用handler,消息類型就是MESSAGE_POST_PROGRESS,前面看到InternalHandler內部handleMessage是有處理的,最後在UI線程執行onProgressUpdate方法。

舉兩個例子🌰
例子1,默認的串行執行:

        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... voids) {
                Log.i(TAG, "task1 SERIAL_EXECUTOR doInBackground: ");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }.execute();

        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... voids) {
                Log.i(TAG, "task2 SERIAL_EXECUTOR doInBackground: ");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }.execute();

        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... voids) {
                Log.i(TAG, "task3 SERIAL_EXECUTOR doInBackground: ");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }.execute();

執行結果如下,每隔兩秒打印一次,可見是串行執行。

2020-01-16 14:51:40.836 13346-13599/com.hfy.demo01 I/hfy: task1 SERIAL_EXECUTOR doInBackground: 
2020-01-16 14:51:42.876 13346-13598/com.hfy.demo01 I/hfy: task2 SERIAL_EXECUTOR doInBackground: 
2020-01-16 14:51:44.915 13346-13599/com.hfy.demo01 I/hfy: task3 SERIAL_EXECUTOR doInBackground: 

例子2,並行執行-直接使用線程池:

        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... voids) {
                Log.i(TAG, "task1 THREAD_POOL_EXECUTOR doInBackground: ");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... voids) {
                Log.i(TAG, "task2 THREAD_POOL_EXECUTOR doInBackground: ");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... voids) {
                Log.i(TAG, "task3 THREAD_POOL_EXECUTOR doInBackground: ");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

執行結果如下,同時打印出來,可見是並行執行。

2020-01-16 14:51:38.772 13346-13599/com.hfy.demo01 I/hfy: task1 THREAD_POOL_EXECUTOR doInBackground: 
2020-01-16 14:51:38.773 13346-13600/com.hfy.demo01 I/hfy: task2 THREAD_POOL_EXECUTOR doInBackground: 
2020-01-16 14:51:38.774 13346-13601/com.hfy.demo01 I/hfy: task3 THREAD_POOL_EXECUTOR doInBackground: 

總結一下,AsyncTask內部默認 使用串行執行器 串行地 在線程池 執行任務,我們也可以使用executeOnExecutor直接使用線程池並行執行。內部使用handler把進度和結果從線程池切換到UI線程。

1.2 HandlerThread

HandlerThread繼承自Thread,內部已準備了Looper並已開啓循環。所以可以在UI線程使用handler發送任務到HandlerThread中執行,且可以隨意多次發送任務。(而普通thread執行完run方法中的耗時操作就結束了。)當不使用時 如onDestroy中,使用quit()或quitSafely()退出即可。

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    //looper開啓之前可以做一些事情
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        //給當前線程準備Looper實例
        Looper.prepare();
        //加鎖,保證能獲取到looer實例
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        //開啓循環
        Looper.loop();
        mTid = -1;
    }
   
    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 {
                	//獲取不到就等 run中的notifyAll()被調用
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
	//直接退出
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    //安全退出(執行完正在正在執行的任務再退出)
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    public int getThreadId() {
        return mTid;
    }
}

舉個例子🌰

    private void testHandlerThread() {
        HandlerThread handlerThread = new HandlerThread("HandlerThreadName");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 1000:
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        Log.i(TAG, "handleMessage: thread name="+Thread.currentThread().getName()+",what="+msg.what);
                        break;
                    case 1001:
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        Log.i(TAG, "handleMessage: thread name="+Thread.currentThread().getName()+",what="+msg.what);
                        break;
                    default:
                        break;
                }
            }
        };

        Log.i(TAG, "sendMessage thread name="+Thread.currentThread().getName());
        handler.sendMessage(Message.obtain(handler, 1000));
        handler.sendMessage(Message.obtain(handler, 1001));
    }

可見在主線程發送了兩個任務,順序執行在HandlerThread了。

2020-01-16 17:12:46.832 16293-16293/com.hfy.demo01 I/hfy: sendMessage thread name=main
2020-01-16 17:12:48.833 16293-17187/com.hfy.demo01 I/hfy: handleMessage: thread name=HandlerThreadName,what=1000
2020-01-16 17:12:49.834 16293-17187/com.hfy.demo01 I/hfy: handleMessage: thread name=HandlerThreadName,what=1001

1.3 IntentService

IntentService是繼承自Service的抽象類,可執行後臺耗時任務,執行完後會自動停止。
因爲是Service,即是Android的組件,優先級比單純的線程高,不容易被系統殺死,所以可用來執行優先級高的後臺任務,

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;
	
	//內部類handler
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
        	//先回調出去,待IntentService子類覆寫自己的邏輯
            onHandleIntent((Intent)msg.obj);
            //結束service自己,msg.arg1是標記某次startService的Id。
            //但如果此時外部又調用了startService,那麼最新的請求id就不是msg.arg1了,所以下面這句就不會結束service。
            stopSelf(msg.arg1);
        }
    }
	//name是線程名
    public IntentService(String name) {
        super();
        mName = name;
    }
    ...
    @Override
    public void onCreate() {
        super.onCreate();
        //創建HandlerThread實例 並啓動
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
		//創建對應looper的handler,所以mServiceHandler的handleMessage執行在 線程中
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
    	//發送消息,參數是startId、intent
    	//每次startService() 都會走這裏
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
    
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
    	//退出looper循環
        mServiceLooper.quit();
    }

    ...

     //執行子在線程,同時只存在一個intent(因爲looper的隊列),所以如果此方法執行時間過長,會阻塞其他請求,所有請求執行完,service會自動停止,所以不能手動調用stopSelf。
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

onCreate中創建了HandlerThread實例,對應的Handler實例mServiceHandler,所以mServiceHandler發送的任務都會在線程中執行。
onStartCommand中調用的是onStart,onStart中確實使用mServiceHandler發送消息,攜帶的參數是startId、intent,startId是每次啓動service的標記,intent就是啓動service的intent。
onDestroy中退出looper循環。

發送的消息在哪處理的呢?
那就看ServiceHandler,其繼承自Handler,handleMessage方法中先調用了抽象方法onHandleIntent((Intent)msg.obj),參數就是啓動service的intent。所以IntentService 的子類必須要重寫onHandleIntent,並處理這個intent。因爲mServiceHandler拿到的HandlerThread的looper,所以這個onHandleIntent()就是執行在子線程中的。
接着調用了stopSelf(msg.arg1),msg.arg1)就是前面說的啓動service的標記。對stopSelf(int startId)說明如下:

startId:代表啓動服務的次數,由系統生成。
stopSelf(int startId):在其參數startId跟 最後啓動該service時生成的ID相等時纔會執行停止服務。

stopSelf():直接停止服務。

使用場景: 如果同時有多個服務啓動請求發送到onStartCommand(),不應該在處理完一個請求後調用stopSelf();因爲在調用此函數銷燬service之前,可能service又接收到新的啓動請求,如果此時service被銷燬,新的請求將得不到處理。此情況應該調用stopSelf(int startId)。

所以,當多次啓動service,就會多次調用 onStart,那麼會有多個任務發出,當每次任務執行完onHandleIntent時,stopSelf(int startId)中會判斷,若又啓動service那麼就不會停止。那麼looper繼續取下個消息繼續處理。直到stopSelf中的startId和最新啓動的startId相同,就會停止。因爲是looper,所以這些任務都是按啓動service的順序執行的。

舉個例子🌰

    private void testIntentService() {

        Intent intent= new Intent(this, MyIntentService.class);
        intent.putExtra("task_name","task1");
        startService(intent);

        intent.putExtra("task_name","task2");
        startService(intent);

        intent.putExtra("task_name","task3");
        startService(intent);
    }

    public static class MyIntentService extends IntentService {

        public MyIntentService() {
            super("MyIntentServiceThread");
        }

        @Override
        protected void onHandleIntent(Intent intent) {            
        	Log.i(TAG, "MyIntentService onHandleIntent: begin."+intent.getStringExtra("task_name"));
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.i(TAG, "MyIntentService onHandleIntent: done."+intent.getStringExtra("task_name"));
        }

        @Override
        public void onDestroy() {
            Log.i(TAG, "MyIntentService onDestroy: ");
            super.onDestroy();
        }
    }
2020-01-17 09:58:44.639 11117-11236/com.hfy.demo01 I/hfy: MyIntentService onHandleIntent: begin.task1
2020-01-17 09:58:46.640 11117-11236/com.hfy.demo01 I/hfy: MyIntentService onHandleIntent: done.task1
2020-01-17 09:58:46.641 11117-11236/com.hfy.demo01 I/hfy: MyIntentService onHandleIntent: begin.task2
2020-01-17 09:58:48.642 11117-11236/com.hfy.demo01 I/hfy: MyIntentService onHandleIntent: done.task2
2020-01-17 09:58:48.644 11117-11236/com.hfy.demo01 I/hfy: MyIntentService onHandleIntent: begin.task3
2020-01-17 09:58:50.645 11117-11236/com.hfy.demo01 I/hfy: MyIntentService onHandleIntent: done.task3
2020-01-17 09:58:50.650 11117-11117/com.hfy.demo01 I/hfy: MyIntentService onDestroy:

靜態內部類MyIntentService繼承IntentService並重寫了onHandleIntent,就是睡兩秒。然後不間斷連續啓動3次,由日誌可見是順序執行的,最後都執行完才走到onDestroy。

再看,如果是間隔三秒發送呢:

        private void testIntentService() {

        Log.i(TAG, "testIntentService: task1");
        Intent intent= new Intent(this, MyIntentService.class);
        intent.putExtra("task_name","task1");
        startService(intent);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "testIntentService: task2");
                intent.putExtra("task_name","task2");
                startService(intent);
            }
        }, 3000);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "testIntentService: task3");
                intent.putExtra("task_name","task3");
                startService(intent);
            }
        }, 3000);
    }
2020-01-17 10:16:29.335 14739-14739/com.hfy.demo01 I/hfy: testIntentService: task1
2020-01-17 10:16:29.698 14739-14843/com.hfy.demo01 I/hfy: MyIntentService onHandleIntent: begin.task1
2020-01-17 10:16:31.698 14739-14843/com.hfy.demo01 I/hfy: MyIntentService onHandleIntent: done.task1
2020-01-17 10:16:31.701 14739-14739/com.hfy.demo01 I/hfy: MyIntentService onDestroy: 
2020-01-17 10:16:32.371 14739-14739/com.hfy.demo01 I/hfy: testIntentService: task2
2020-01-17 10:16:32.390 14739-14862/com.hfy.demo01 I/hfy: MyIntentService onHandleIntent: begin.task2
2020-01-17 10:16:34.391 14739-14862/com.hfy.demo01 I/hfy: MyIntentService onHandleIntent: done.task2
2020-01-17 10:16:34.451 14739-14739/com.hfy.demo01 I/hfy: MyIntentService onDestroy: 
2020-01-17 10:16:35.339 14739-14739/com.hfy.demo01 I/hfy: testIntentService: task3
2020-01-17 10:16:35.364 14739-14873/com.hfy.demo01 I/hfy: MyIntentService onHandleIntent: begin.task3
2020-01-17 10:16:37.364 14739-14873/com.hfy.demo01 I/hfy: MyIntentService onHandleIntent: done.task3
2020-01-17 10:16:37.367 14739-14739/com.hfy.demo01 I/hfy: MyIntentService onDestroy:

可見每個任務執行完 就走onDestroy了。這是因爲當一個任務執行完,走到stopSelf(int startId)時,後面還沒有再次開啓service,所以此時的stopSelf中的startId就是最新的,所以就會停止服務了。

二、Android中的線程池

線程池優點如下:

  • 能夠重用線程池中的線程,避免線程的創建、銷燬帶來的性能開銷。
  • 能有效控制線程池中的最大併發數,避免大量的線程之間因互相搶佔系統資源而導致的阻塞現象。
  • 能對線程進行簡單管理,並提供定時執行、指定間隔循環執行的功能。
    Android中的線程池來源於Java的Executor,正在的實現是ThreadPoolExecutor。

2.1 ThreadPoolExecutor

ThreadPoolExecutor是線程池的真正實現,看下其構造方法裏的參數,參數會影響線程池的功能特性。

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }
  • corePoolSize,核心線程數,默認一般核心線程會在線程池中一直存活,即使處理空閒狀態。但當allowCoreThreadTimeOut設置爲true,那麼核心線程就會有閒置超時時間,閒置超過超時時間就會終止。超時時間由keepAliveTime和unit指定。
  • maximumPoolSize,最大線程數,當活動線程到達這個數,後續的新任務會被阻塞。
  • keepAliveTime,非核心線程閒置時 的超時時長。非核心線程閒置時間超過此時間就會被回收。當allowCoreThreadTimeOut設置爲true,keepAliveTime也會作用於核心線程。
  • unit,是keepAliveTime的時間單位,取值是枚舉,有TimeUnit.MINUTES、TimeUnit.SECONDS、TimeUnit.MILLISECONDS等。
  • workQueue,線程池中的任務隊列,通過線程池的execute方法提交的Runnable會存在這個隊列中。
  • threadFactory,線程工廠,爲線程池提供創建新線程的能力。

還有個不常用參數RejectedExecutionHandler handler,調用其rejectedExecution()方法來處理 當任務隊列已滿 時 不能執行新任務的情況。handler的默認實現是AbortPolicy,rejectedExecution()中會直接拋出異常RejectedExecutionException。其他實現如DiscardPolicy的rejectedExecution()中是不做任何事。還有CallerRunsPolicy、DiscardOldestPolicy。

ThreadPoolExecutor執行任務的執行規則 如下:

  • 如果線程池中的線程數未到達核心線程數,那麼會直接啓動一個核心線程來執行這個任務。(不管已啓動的核心線程是否空閒)
  • 如果線程池中的線程數已到達或超過核心線程數,那麼任務會插入到任務隊列中排隊等待執行。
  • 如果2中 任務無法插入到隊列中,一般是對隊列已滿,若此時未達到最大線程數,就會啓動非核心線程執行這個任務。
  • 如果3中線程數達到最大線程數,那麼會拒絕執行任務,即會調用RejectedExecutionHandler的rejectedExecution()通知調用者。

我們看下AsyncTask中線程池的配置:

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE_SECONDS = 30;

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);
        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

可見THREAD_POOL_EXECUTOR這個線程池的配置如下:

  • 核心線程數,2-4個
  • 最大線程數,CPU核心數量 * 2 + 1
  • 超時時間30s,允許核心線程超時
  • 隊列容量128

2.2 線程池的分類

Android中常見的4類線程池,都是直接或間接配置ThreadPoolExecutor實現自己特性的,它們是FixedThreadPool、CachedThreadPool、ScheduledThreadPool、SingleThreadExecutor。它們都可以通過工具類Executors獲取。

2.2.1 FixedThreadPool

通過Executors的newFixedThreadPool方法獲得。固定的核心線程數量,沒有非核心線程,空閒時不會被回收,隊列長度無限制。因爲不會被回收,所以能快速執行外界請求

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

2.2.2 CachedThreadPool

通過Executors的newCachedThreadPool方法獲得。核心線程數0,非核心線程數無限制,空閒回收超時時間60s,隊列不能插入任務。當所有線程都處於活動狀態,就會創建新線程處理任務,否則利用空閒線程處理任務。當整個線程池空閒時 所有線程都會被回收,不佔用系統資源。因此,適合執行大量耗時較少的任務

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

2.2.3 ScheduledThreadPool

通過Executors的newScheduledThreadPool方法獲得。固定核心線程數,不限制非核心線程數,非核心線程閒置回收超時時間是10ms。一般用於執行定時任務、固定週期的重複任務

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
   
    private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

2.2.4 SingleThreadExecutor

通過Executors的newSingleThreadExecutor方法獲得。僅有1個核心線程,不會回收。可確保所有任務按順序執行,不用處理線程同步的問題。

    private void testThreadPoolExecutor() {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    Log.i(TAG, "testThreadPoolExecutor: run begin");
                    Thread.sleep(4000);
                    Log.i(TAG, "testThreadPoolExecutor: run end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
        fixedThreadPool.execute(runnable);

        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        cachedThreadPool.execute(runnable);

        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);
        scheduledThreadPool.execute(runnable);
        //延遲2秒執行
        scheduledThreadPool.schedule(runnable, 2, TimeUnit.SECONDS);
        //延遲2秒執行,然後以每次 任務開始的時間計時, 1秒後,如果任務是結束的 就立刻執行下一次;如果沒有結束,就等它結束後立即執行下一次。
        scheduledThreadPool.scheduleAtFixedRate(runnable, 0, 1, TimeUnit.SECONDS);
        //延遲3秒執行,然後以每次任務執行完後的時間計時, 2秒後,執行下一次~
        scheduledThreadPool.scheduleWithFixedDelay(runnable,1,2,TimeUnit.SECONDS);

        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        singleThreadExecutor.execute(runnable);
    }

注意點:scheduledThreadPool使用時注意區分scheduleAtFixedRate、scheduleWithFixedDelay的循環任務的邏輯區別

  • scheduleAtFixedRate,以每次 任務開始的時間計時, period時間後,如果任務是結束的就立刻執行下一次;如果沒有結束,就等它結束後立即執行下一次。
  • scheduleWithFixedDelay,以每次任務執行完後的時間計時,period時間後,執行下一次。

並且,這兩個方法都是在任務結束後才執行下一次,那麼如果某個任務發生異無法執行完,那麼整個循環任務就會失效。所以需要給任務添加超時機制(比如給任務加上try-catch-finally,catch住超時異常) 保證任務即使發生異常也可以結束,就可保證循環正常執行了。

發佈了54 篇原創文章 · 獲贊 16 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章