Android的線程和線程池

前言

線程在Android中是一個很重要的概念,從用途上來講,線程分爲主線程和子線程,主線程主要處理和界面相關的事情,而子線程則每每用於執行耗時操做。因爲Android的特性,若是在主線程中執行耗時操做那麼就會致使程序沒法及時地響應,所以耗時操做必須放在子線程中去執行。android

在操做系統中,線程是操做系統調度的最小單元,同時線程又是一種受限的系統資源,即線程不可能無限制的產生,而且線程的建立和銷燬都會有相應的開銷。檔系統中存在大量的線程時,系統會經過時間片輪轉的方式調度每一個線程,所以線程不可能作到絕對的並行,除非線程數量小於等於CPU核心數,通常來講這是不可能的。正確的作法是採用線程池,一個線程池會緩存必定數量的線程,經過線程池就能夠避免由於頻繁建立和銷燬線程所帶來的系統開銷。編程

主線程和子線程

Android沿用了Java的線程模型,其中的線程也分爲主線程和子線程,其中主線程也叫UI線程。主線程的做用是運行四大組件以及處理它們和用戶的交互,而子線程的做用則是執行耗時任務,好比網絡請求、I/O操做等。從Android3.0開始系統要求網絡訪問必須在子線程中進行,不然網絡訪問將會失敗並拋出NetworkOnMainThreadException這個異常,這樣作事爲了不主線程因爲耗時操做所阻塞而出現ANR異常。緩存

Android中的線程形態

除了傳統的Thread線程外,Android還提供了AsyncTask、HandlerTask以及IntentService,這三者的底層實現也是線程,但它們具備特殊的表現形式,同時在使用上也各有優缺點。網絡

AsyncTask

AsyncTask是一種輕量級的異步任務類,它能夠在線程池中執行後臺任務,而後把執行的進度和最終結果傳遞給主線程並在主線程上更新UI。是實現上來講,AsyncTask封裝了Thread和Handler,經過AsyncTask能夠更加方便地執行後臺任務以及在主線程中訪問UI,可是AsyncTask並不適合進行特別耗時的任務,對應特別耗時的任務來講,建議使用線程池。併發

AsyncTask使用

AsyncTask是一個抽象的泛型類,它提供了Params、Progress和Result這三個泛型參數,其中Params表示輸入參數的類型,Progress表示後臺任務的執行進度的類型,而Result則表示後臺任務返回結果的類型,若是AsyncTask確實不須要傳遞具體的參數,那麼這三個泛型能夠用Void來代替。聲明以下:異步

public abstract class AsyncTask<Params,Progress,Result>

AsyncTask提供了4個核心方法,它們的含義以下圖所示ide

  1. onPreExecute(),在主線程中執行,在異步任務執行以前會調用此方法,通常能夠用於作一些準備工做。oop

  2. doInBackground(Params...params),在線程池中執行,用於執行異步任務,params表示異步任務的輸入參數。在該方法中能夠經過調用publishProgress方法來更新任務的進度,由於publishProgress會調用onProgressUpdate方法。post

  3. onProgressUpdate(Progress...values),在主線程中執行,當後臺任務的執行進度發生改變時此方法會被調用。性能

  4. onPostExecute(Result result),在主線程中執行,在異步任務執行以後,次方法會被調用,其中result參數是後臺任務的返回值,即doInBackground的返回值。

上述方法中,onPreExecute先執行,而後是doInBackground,最後纔是onPostExecute。此外AsyncTask還提供了onCancelled()方法,它一樣在主線程中執行,當異步任務被取消時,onCancelled()方法會被調用,這個時候onPostExecute則不會被調用。

下面代碼爲AsyncTask的一個應用實例:

private class LoadRecordTask extends
            AsyncTask<Object, VideoInfo, List<VideoInfo>> {

        @Override
        protected void onPreExecute() {
            // TODO Auto-generated method stub
            super.onPreExecute();
            mDescLoad.setVisibility(View.VISIBLE);
            mDescLoad.setText(R.string.refreshing);
            mVideoRecords.setEnabled(false);
        }

        @Override
        protected List<VideoInfo> doInBackground(Object... params) {
            // TODO Auto-generated method stub
            videoInfos = (ArrayList<VideoInfo>) MediaContentResolverUtils
                    .getVideoInfoList(RecordVideoActivity.this);

            mVideoThumbnailMap = (HashMap<String, String>) mVideoThumbnailDao
                    .findAllToMap();

            if (videoInfos == null || videoInfos.size() == 0) {
                return videoInfos;
            }

            // 沒有縮略圖 獲取縮略圖
            for (VideoInfo info : videoInfos) {

                String md5Name = Md5Utils.encode(info.getFileTitle());

                if (!mVideoThumbnailMap.containsKey(md5Name)) {
                    //數據處理
                }

                publishProgress(info);

                if (isCancelled()) { //異步任務取消時會調用
                    break;
                }
            }

            return videoInfos;
        }

        @Override
        protected void onProgressUpdate(VideoInfo... values) {
            // TODO Auto-generated method stub
            super.onProgressUpdate(values);

            for (VideoInfo info : values) {
                //UI更新進度
            }
        }

        @Override
        protected void onPostExecute(List<VideoInfo> result) {

            //取得後臺任務的結果,更新UI
        }

        /**
         * 運行在UI線程,調用cancel()方法後觸發,在doInBackground()方法結束後執行
         */
        @Override
        protected void onCancelled(List<VideoInfo> result) {
            // TODO Auto-generated method stub
            super.onCancelled(result);
        }
    }

運行和取消該任務的代碼以下:

mLoadRecordTask = new LoadRecordTask();
    mLoadRecordTask.execute();
    mLoadRecordTask.cancel(true); //結束任務

AsyncTask條件限制

  • AsyncTask的類必須在主線程中加載

  • AsyncTask的對象必須在主線程中建立

  • execute方法必須在UI線程調用

  • 不要在程序中直接調用onPreExecute()、onPostExecute()、doInBackgroud()和onProgressUpdate()

  • 一個AsyncTask對象只能執行一次,即只能調用一次execute方法,不然會報運行時異常

  • 在Android1.6以前,AsyncTask是串行執行任務的,Android1.6的時候AsyncTask開始採用線程池裏處理並行任務,但從Android3.0開始,爲了不AsyncTask所帶來的併發錯誤,AsyncTask又採用一個線程來串行執行任務。儘管如此,在Android3.0及之後版本中,咱們仍然能夠經過AsyncTask的executeOnExecutor方法(不能向下兼容)來並行的執行任務

AsyncTask工做原理

咱們從AsyncTask的execute方法開始分析,execute方法又會調用ecuteOnExecutor方法,它們的實現以下:

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

    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;
    }

上述代碼中,sDefaultExecutor其實是一個串行的線程池,一個進程中全部的AsyncTask所有在這個串行的線程池中排隊執行。在executeOnExecutor方法中,AsyncTask的onPreExecute()最早執行,而後線程池開始執行。下面分析線程池的執行過程,以下所示:

public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };
        
        //將AsyncTask的Params參數封裝到FutureTask對象中,FutureTask的run方法會調用mWorker的call方法
        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 occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); //CPU核心數
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1; //核心工做線程
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; //最多工做線程
    private static final int KEEP_ALIVE = 1; //空閒線程的超時時間爲1秒

    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();  
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

    //實現一個線程池
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        //線程同步
        public synchronized void execute(final Runnable r) {
            //將任務r插入mTasks任務隊列中
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run(); //執行任務
                    } finally {
                        scheduleNext(); //繼續執行下一個任務
                    }
                }
            });
            
            //沒有真正活動的AsyncTask時調用
            if (mActive == null) {
                scheduleNext(); 
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive); //真正執行任務
            }
        }
    }

從SerialExecutor的實現能夠分析AsyncTask的排隊執行狀況。首先系統會將AsyncTask的Params參數封裝到FutureTask對象中,FutureTask是一個併發類,在這裏它充當了Runnable的做用(FutureTask實現了Runnable方法)。接着這個FutureTask即mFuture會交給SerialExecutor的execute方法去處理。SerialExecutor的execute方法首先會把FutureTask對象添加到任務隊列mTasks中,若是當前沒有正在活動的AsyncTask任務,那麼就會調用SerialExecutor的scheduleNext方法來執行下一個AsyncTask任務,不然等待當前AsyncTask任務完成再繼續執行新的AsyncTask任務,直到全部的AsyncTask任務執行完畢。從這能夠看出,AsyncTask是串行執行任務的

AsyncTask中有兩個線程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一個Handler(InternalHandler),其中線程池SerialExecutor用於執行任務的排隊,線程池THREAD_POOL_EXECUTOR用於真正地執行AsyncTask任務,InternalHandler用於將執行環境從線程池切換到主線程。在AsyncTask的構造方法中有以下這麼一段代碼,因爲FutureTask的run方法調用mWorker的call方法,所以mWorker的call方法最終會在線程池中執行。

public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true); //表示當前任務以及調用過了
    
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams)); //執行doInBackground方法
            }
        };
        
        //將AsyncTask的Params參數封裝到FutureTask對象中,FutureTask的run方法會調用mWorker的call方法
        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 occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

在mWorker的call方法中,首先將mTaskInvoked設爲true,表示當前任務以及被調用了,而後執行AsyncTask的doInBackground方法,接着將其返回值傳遞給postResult方法,它的實現以下:

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

在上面的代碼中,postResult方法會經過sHandler發送一個MESSAGE_POST_RESULT的消息,這個sHandler的定義以下所示:

private static InternalHandler sHandler;

    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

    private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());
        }
    
        @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;
            }
        }
    }

能夠發現,sHandler是一個靜態的Handler類對象,爲了可以將執行環境切換到主線程,這就sHandler這個對象必須在主線程中建立。因爲靜態成員會在加載類的時候進行初始化,所以這就變相要求AsyncTask的類必須在主線程中加載,不然同一進程中的AsyncTask都將沒法正常工做。sHandler收到sHandlerMESSAGE_POST_PROGRESS會調用onProgressUpdate方法更新進度,收到MESSAGE_POST_RESULT這個消息後會調用AsyncTask的finish方法,以下

private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

AsyncTask的finish方法會判斷AsyncTask是否取消執行了,是則調用onCancelled方法,不然調用onPostExecute(result),此時doInBackground的返回結果會傳遞給onPostExecute方法,最後將任務狀態mStatus置爲完成。至此AsyncTask的整個過程就分析完成了。

經過分析AsyncTask的源碼,能夠進一步肯定,從Android3.0開始,默認狀況下AsyncTask的確是串行執行。咱們仍然能夠經過AsyncTask的executeOnExecutor方法(不能向下兼容)來並行的執行任務。

HandlerThread

HandlerThread繼承了Thread,它是一種可使用Handler的Thread,它的實現也很簡單,就是在run方法中經過Looper.prepare()來建立消息隊列,並經過Looper.loop()來開啓消息循環,這樣在實際的使用中就容許HandlerThread中建立Handler。HandlerThread的run方法以下所示:

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

從HandlerThread的實現來看,它和普通的Thread有顯著的不一樣之處。普通Thread主要同於run方法中執行一個耗時任務,而HandlerThread在內部建立了消息隊列,外界須要經過Handler的消息方式來通知HandlerThread執行一個具體任務。HandlerThread是個頗有用的類,它在Android中的一個具體的使用場景是IntentService。因爲HandlerThread的run方法是一個無限循環,所以當明確不須要再使用HandlerThread時,能夠經過它的quit或者quitSafely方法來終止線程的執行,這是一個好的編程習慣。示例代碼以下:

public class HandlerThreadDemo extends Activity {
    
        private Looper mLooper;
        private MyHandlerThread mHandlerThread;
        private TextView mInfoTxt;
        
        private Handler mHandler;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_thread);
            
            mInfoTxt  = (TextView) findViewById(R.id.tv_info);
            
            mHandlerThread = new MyHandlerThread("mHandlerThread");
            mHandlerThread.start(); //先start
            mLooper = mHandlerThread.getLooper();
            
            //註冊到Handler,經過Handler發送消息
            mHandler = new Handler(mLooper,mHandlerThread);
            
        }
        
        public void click(View view) {
            
            mHandler.sendEmptyMessage(1);
            
        }
        
        private class MyHandlerThread extends HandlerThread implements Callback {
    
            public MyHandlerThread(String name) {
                super(name);
                // TODO Auto-generated constructor stub
            }
    
            @Override
            public boolean handleMessage(Message msg) {
                // TODO Auto-generated method stub
                
                if (msg.what == 1) {
                    System.out.println("mHandlerThread");
                    mInfoTxt.setText("mHandlerThread");
                }
                
                return true;
            }
        }
    }

IntentService

IntentService是一種特殊的Service,它繼承了Service而且它是一個抽象類,所以必須建立它的子類才能使用IntentService。IntentService可用於執行後臺耗時的後臺,當任務執行後它會自動中止,同時因爲IntentService是服務的緣由,這致使它的優先級比單純的線程要高不少,因此IntentService比較適合執行一些高優先級的後臺任務,由於它的優先級高不容易被系統殺死。在實現上,IntentService封裝了HandlerThread和Handler,這一點能夠在它的onCreate方法中看出來,以下所示。

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);
    }

當IntentService被第一次啓動時,它的onCreate方法會被調用,onCreate方法會建立一個HandlerThread,而後使用它的Looper來構造一個Handler對象mServiceHandler,這樣經過mServiceHandler發送的消息最終都會在HandlerThread中執行,從這個角度來看,IntentService也能夠用於執行後臺任務。每次啓動IntentService,它的onStartCommand方法就會調用一次,IntentService在onStartCommand中處理每一個後臺任務的Intent。下面看一下onStartCommand方法是如何處理外界Intent的,onStartCommand調用了onStart,onStart方法的實現以下所示:

public void onStart(Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

能夠看出,IntentService僅僅是經過mServiceHandler發送了一個消息,這個消息會在HandlerThread中去處理。mServiceHandler收到消息後,會將Intent對象對象傳遞給onHandleIntent方法去處理。注意這個Intent對象的內容和外界的startService(intent)中的intent的內容是徹底一致的,經過這個Intent對象便可解析出外界啓動IntentService時所傳遞的參數,經過這些參數就能夠區分具體的後臺任務,這樣在onHandleIntent方法中就能夠對不一樣的後臺任務作處理了。當onHandleIntent方法執行結束後,IntentService會經過stopSelf(int startId)來嘗試中止服務。這裏之因此採用stopSelf(int startId)而不是stopSelf()來中止服務,是由於stopSelf()會馬上中止服務,而這個時候還可能有其餘消息未處理,stopSelf(int startId)則會等待全部的消息都處理完畢後才終止服務。通常來講,stopSelf(int startId)在嘗試中止服務以前會判斷最近啓動服務的次數是否和startId相等,若是相等就馬上中止服務,不相等則不中止服務,這個策略能夠從AMS的stopServiceToken方法的實現中找到依據。ServiceHandler的實現以下所示:

private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        onHandleIntent((Intent)msg.obj);
        stopSelf(msg.arg1);
    }
}

IntentService的onHandleIntent方法是一個抽象方法,它須要咱們在子類中實現,它的做用是從Intent參數中區分具體的任務並執行這些任務。若是目前只存在一個後臺任務,那麼onHandleIntent(Intent)方法執行完這個任務後,stopSelf(int startId)就會直接中止服務;若是目前存在多個後臺任務,那麼當onHandleIntent方法執行完最後一個任務時,stopSelf(int startId)纔會直接中止服務。另外,因爲沒執行一個後臺任務就必須啓動一次IntentService,而IntentService內部則經過消息的方式向HandlerThread請求執行任務,Handler中的Looper是順序處理消息的,這就意味着IntentService也是順序執行後臺任務,當有多個後臺任務同時存在時,這些後臺任務會按照外界發起的順序排隊執行。

下面經過一個示例來講明IntentService的工做方式,首先派生一個IntentService的子類,它的實現以下所示:

public class LocalIntentService extends IntentService {

    public LocalIntentService() {
        super("LocalIntentService");
        // TODO Auto-generated constructor stub
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        System.out.println("onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // TODO Auto-generated method stub
        String action = intent.getStringExtra("task");
        System.out.println("action: " + action);
        SystemClock.sleep(3000); //休眠模擬耗時的後臺任務
        if (action.equals("task1")) {
            System.out.println("handle action: " + action);
        }

        if (action.equals("task2")) {
            System.out.println("handle action: " + action);
        }
        
        if (action.equals("task3")) {
            System.out.println("handle action: " + action);
        }
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        System.out.println("onDestroy");
        super.onDestroy();
        
    }
}

LocalIntentService實現完成之後,就能夠在外界請求執行後臺任務了,下面在Activity中發起3個後臺任務的請求,以下所示:

Intent service = new Intent(this, LocalIntentService.class);
    service.putExtra("task", "task1");
    startService(service);
    service.putExtra("task", "task2");
    startService(service);
    service.putExtra("task", "task3");
    startService(service);

運行程序,觀察日記以下

22:14:19.407: I/System.out(16384): onStartCommand
01-08 22:14:19.407: I/System.out(16384): action: task1
01-08 22:14:19.407: I/System.out(16384): onStartCommand
01-08 22:14:19.408: I/System.out(16384): onStartCommand
01-08 22:14:22.407: I/System.out(16384): handle action: task1
01-08 22:14:22.409: I/System.out(16384): action: task2
01-08 22:14:25.410: I/System.out(16384): handle action: task2
01-08 22:14:25.418: I/System.out(16384): action: task3
01-08 22:14:28.418: I/System.out(16384): handle action: task3
01-08 22:14:28.429: I/System.out(16384): onDestroy

從日誌能夠看出,三個後臺任務是排隊執行的,它們的執行順序就是它們發起請求對的順序。當task3執行完畢後,LocalIntentService才真正地中止,執行了onDestroy方法。

Android中的線程池

線程池的有點主要有三點:

  • 重用線程池中的線程,避免由於線程的建立和銷燬所帶來的性能開銷

  • 能有效控制線程池的最大併發數,避免大量的線程之間因互相搶佔系統資源而致使的阻塞現象。

  • 可以對線程進行簡單的管理,並提供定時執行以及指定間隔循環執行等功能。

Android中的線程池概念來源於Java中的Executor,Executor是一個接口,真正的線程池的實現爲ThreadPoolExecutor。ThreadPoolExecutor提供了一系列參數來配置線程池,經過不一樣的參數能夠建立不一樣的線程池。因爲Android中的線程池都是直接或者間接經過配置ThreadPoolExecutor來實現的,所以須要先介紹ThreadPoolExecutor。

ThreadPoolExecutor

ThreadPoolExecutor是線程池的真正實現,它的構造方法提供了一系列參數來配置線程池,下面介紹ThreadPoolExecutor的構造方法中各個參數的含義,這些參數將會直接影響到線程池的功能特性,下面是ThreadPoolExecutor的一個比較經常使用的構造方法。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
    }
  • corePoolSize

線程池的核心線程數,默認狀況下,核心線程會在線程池中一直存活,即便他們處於閒置狀態。若是將ThreadPoolExecutor的allowCoreThreadTimeout屬性設置爲true,那麼閒置的核心線程在等待新任務到來會有超時策略,這個時間間隔由keepAliveTime所指定,當等待時間超時keepAliveTime所指定的時長後,核心線程會被終止。

  • maximumPoolSize

線程池所能容納的最大線程數,當活動線程達到這個數值後,後續的新任務將會被阻塞。

  • keepAliveTime

非核心線程閒置時的超時時長,超過這個時間,非核心線程就會被收回。當ThreadPoolExecutor的allowCoreThreadTimeout屬性設置爲true時,keepAliveTime一樣會做用於核心線程。

  • unit

用於指定keepAliveTime參數的時間單位,這是一個枚舉,經常使用的有TimeUnit.MILLISECONDS;TimeUnit.SECONDS;TimeUnit.MINUTES等

  • workQueue

線程池的任務隊列,經過線程池的execute方法提交的Runnable對象會存儲在這個參數中。

  • ThreadFactory

線程工廠,爲線程池提供建立新線程的功能。ThreadFactory是一個接口,它只有一個方法:Thread newThread(Runnable r);

除上面的這些主要的參數外,ThreadPoolExecutor還有一個不經常使用的參數RejectedExecutionHandler。當線程池沒法執行新的任務時,這多是因爲任務隊列已滿或者是沒法成功執行任務,這個時候ThreadPoolExecutor回調用RejectedExecutionHandler的rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法來通知調用者,默認狀況下rejectedExecution會直接拋出一個RejectedExecutionException的運行時異常。ThreadPoolExecutor爲RejectedExectutionHandler提供了幾個可選值:CallerRunsPolicy、AbortPolicy、DiscardPolicy、DiscardOldestPolicy,其中AbortPolicy是默認值,可是RejectedExecutionHandler這個參數不經常使用。

ThreadPoolExecutor執行任務時大體遵循以下規則:

  • 若是線程池中的線程數量未達到核心線程的數量,那麼會直接啓動一個核心線程來執行任務

  • 若是線程池中的線程數量已經達到或超過核心線程的數量,那麼任務會被插入到任務隊列中排隊等待執行。

  • 若是在步驟2中沒法將任務插入到任務隊列中,這每每是因爲任務隊列已滿,這個時候若是線程數量未達到線程池規定的最大值,那麼會馬上啓動一個非核心線程來執行任務。

  • 若是步驟3中線程數量已經達到線程池中規定的最大值,那麼就拒絕執行此任務,ThreadPoolExecutor會調用RejectedExecutionHandler的rejectedExecution方法來通知調用者。

ThreadPoolExecutor的參數配置在AsyncTask中有明顯的體現,下面是AsyncTask中的線程池配置狀況

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;

    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
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

AsyncTask線程池配置後的規格以下:

  • 核心線程數等於CPU核心數+1

  • 線程池的最大線程數爲CPU的核心數的2倍 + 1

  • 核心線程無超時機制,非核心線程在閒置時的超時時間爲1秒

  • 任務隊列的容量爲128

線程池的分類

  • FixedThreadPool

經過Executors的newFixedThreadPool方法來建立。它是一種線程數量固定的線程池,當線程處於空閒狀態時,它們並不會被收回,除非線程池關閉了。當全部的線程都處於活動狀態時,新任務都會處於等待狀態,直到有線程空閒出來。因爲FixedThreadPool只有核心線程而且這些核心線程都不會被回收,這意味着它可以更加快速的響應外界的請求。實現以下,能夠發現FixedThreadPool中只有核心線程而且這些核心線程沒有超時機制,另外任務隊列也是沒有大小限制的

/*
     * @param nThreads the number of threads in the pool
     * @return the newly created thread pool
     * 
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
  • CachedThreadPool

經過Executors的newCachedThreadPool方法來建立。它是一種線程數量不定的線程池,它只有非核心線程,而且最大線程數爲Integer.MAX_VALUE。因爲Integer.MAX_VALUE是一個很大的數,實際上就至關於最大線程數能夠任意大。當線程池中的線程都處於活動狀態時,線程池會建立新的線程來處理新任務,不然就會利用空閒的線程來處理新的任務。線程池中的空閒線程都有超時機制,這個超時時長爲60秒,超過60秒閒置線程就會被回收。和FixedThreadPool不一樣的是,CachedThreadPool的任務隊列其實至關於一個空集合,這將致使任何任務都會當即被執行,由於這種狀況下SynchronousQueue是沒法插入任務的。SynchronousQueue是一個很是特殊的隊列,不少狀況下能夠理解爲一個沒法存儲元素的隊列(實際中不多使用)。從CachedThreadPool的特性來看這類線程池比較適合執行大量的耗時較少的任務。當整個線程池都處於閒置狀態時,線程池中的線程都會超時而被中止,這個時候CachedThreadPool之中是沒有任何線程的,它幾乎不佔用任何系統資源的。

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

經過Executors的newScheduledThreadPool方法來建立。它的核心線程數量是固定的,而非核心線程數量是沒有限制的,而且當核心線程閒置時會被當即收回。ScheduledThreadPoll這類線程主要用於執行定時任務和具備固定週期的重複任務

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue(), threadFactory);
}
  • SingleThreadExecutor

經過Executors的newSingleThreadExecutor方法來建立。這類線程池內部只有一個核心線程,它確保全部的任務都在同一個線程中順序執行。SingleThreadExecutor的意義在於統一全部的外界任務到一個線程中,這使得在這些任務之間不須要處理線程同步的問題。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
相關文章
相關標籤/搜索