一、介绍

1、Android 中的线程

线程在 Android 中是一个很重要的概念,从用途上说,线程分为主线程和子线程,主线程主要处理和界面相关的事情,而子线程则往往用于执行耗时操作。在 Android 中,除了 Thread 之外,扮演子线程的还有 AsyncTask 和 IntentService,同时 HandlerThread 也是一种特殊的线程。尽管 AsyncTask、IntentService 以及 HandlerThread 的表现形式都有别与传统的线程,但是它们的本质仍然是传统的线程。对于 AsyncTask 来说,它的底层用到了线程池,对于 IntentService 和 HandlerThread 来说,它们底层则直接使用了线程池。

不同形式的线程虽然都是线程,但是他们具有不同的特性和使用场景。AsyncTask 封装了线程池和 Handler,主要用来在子线程中更新 UI。 HandlerThread 是一种具有消息循环的线程,在它的内部可以使用 Handler。 IntentService 是一个服务,系统对其进行了封装使其可以更方便的执行后台任务,IntentService 内部采用 HandlerThread 执行任务,当任务执行完毕之后 IntentService 会自动退出。从任务执行的角度来看, IntentService 的作用很像一个后台线程,但是 IntentService 是一种服务。它不容易被系统杀死从而可以尽量保证任务的执行,而如果是一个后台线程,由于这个时候进程中没有活动的四大组件,那么这个进程的优先级就会非常低,会很容易被系统杀死。

2、主线程和子线程

主线程是指进程所拥有的线程,在 Java 中默认情况下一个进程只有一个线程,这个线程就是主线程。主线程主要处理界面交互相关的逻辑,因为用户随时会和界面发生交互,因此主线程在任何时候都必须有较高的响应速度,因此耗时任务需要在后台执行,也就是开子线程。 Android 沿用了 Java 的线程模型,其中的线程也分为主线程和子线程,其中主线程也就是 UI 线程,主线程的作用是运行四大组件已经处理它们和用户的交互,而子线程的作用则是执行耗时任务,比如网络请求,I/O 操作 等。

二、Android 中的线程的形态

1、 AsyncTask

AsyncTask 是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新 UI。从实现上来说,AsyncTask 封装了 Thread 和 Handler,通过 AsyncTask 可以更加方便地执行后台任务以及在主线程中访问 UI,但是 AsyncTask 并不适合特别耗时的后台任务,对于特别耗时的任务来说,应该使用线程池。

AsyncTask 是一个抽象的泛型类,它提供了 Params、Progress 和 Result 这三个泛型参数。其中 Params 表示参数的类型,Progress 表示后台任务的执行进度的类型,而 Result 则表示后台任务的返回结果的类型,如果 AsyncTask 确实不需要传递具体的参数,那么这三个泛型参数可以用 Void 来代替。AsyncTask 这个类的声明如下:

public abstract class AsyncTask

AsyncTask 提供了 4 个核心方法,它们的含义如下所示:
(1)、 onPreExecure(),在主线程中执行,在异步任务执行之前,此方法会被调用,一般可以用于做一些准备工作。

(2)、doInBackground(Params... params),在线程中执行,此方法用于执行异步任务,params 参数表示异步任务的输入参数。在此方法中可以通过 publishProgress 方法来更新任务的进度,publishProgress 方法会调用 onProgressUpdate 方法。另外此方法需要返回计算结果给 onPostExecute 方法。

(3)、 onProgressUpdate(Progress..values),在主线程执行,当后台任务的执行进度发生改变时此方法会被调用。

(4)、onPostExecute(Result result),在主线程中执行,在异步任务执行之后,此方法会被调用,其中 result 参数是后台任务的返回值,即 doInBackground 的返回值。

上面这几个方法,onPreExecute 先执行,接着是 doInBackground,最后才是 onPostExecute。除了上述四个方法之外,AsynTask 还提供了 onCanceled() 方法,它同样在主线程执行,当异步任务被取消,onCancelled() 方法,它同样在主线程中执行,当异步任务被取消时,onCancelled() 方法会被调用,这个时候 onPostExecute 则不会被调用。下面提供一个典型的示例,如下所示:

private class DownloadFilesTask extends AsyncTask {        @Override    protected Long doInBackground(URL... urls) {        int count = urls.length;        long totalSize = 0;        for (int i = 0; i < count; i++) {            totalSize += Downloader.downloadFile(urls[i]);            publishProgress((int) ((i / (float) count) * 100));            if (isCancelled()) break;        }        return totalSize;    }    @Override    protected void onProgressUpdate(Integer... progress) {        setProgressPercent(progress[0]);    }    @Override    protected void onPostExecute(Long result) {        showDialog("Downloaded " + result + " bytes");    }}

在上面的代码中,实现了一个具体的 AsyncTask 类,这个类主要用于模拟文件的下载过程,它的输入参数为 URL,后台任务的进程参数为 Integer,而后台任务的返回结果为 Long 类型。注意到 doInBackground 和 onProgressUpdate 方法它们的参数中均包含... 的字样,在 Java 中 ... 表示参数的数量不定,它是一种数组型参数。当要执行上述下载任务时,可以通过如下方法来完成:

new DownloadFilesTask().execute(url1, url2, url3);

在 DownloadFilesTask 中,doInBackground 用来执行具体的下载任务并通过 publishProgress 方法来更新下载的进度,同时还要判断下载任务是否被外界取消。当下载任务完成后,doInBackground 会返回结果,即下载的总字节数。需要注意的是,doInBackground 是在线程池中执行的。onProgressUpdate 用于更新界面中下载的进度,它运行在主线程,当 publishProgress 被调用时,此方法就会被调用。当下载任务完成后,onPostExecute 方法就会被调用,它也是运行在主线程中,这个时候我们就可以在界面上做出一些提示,比如弹出一个对话框提示用户下载已经完成。

AsncTask 在具体的使用过程中也是有一些条件限制,主要如有如下几点:
1、AsyncTask的类必须在主线程中进行加载,当在4.1之后这个过程会自动进行;

2、AsyncTask的对象必须在主线程中创建;

3、execute方法必须在UI线程中被调用;

4、不要直接调用onPreExecute, doInBackground, onProgressUpdate和onPostExecute;

5、一个AsyncTask对象的execute方法只能被调用一次;

6、Android 1.6之前,AsyncTask是串行执行任务的;1.6采用线程池处理并行任务;从3.0开始,又采用一个线程来串行执行任务。

7、3.0之后可以用executeOnExecutor来并行地执行任务,如果我们希望在3.0之后能并行地执行上面的任务,那么我们应该这样去写:

 new DownloadFilesTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url1, url2, url3);
2、AsyncTask 工作原理

为了分析 AsyncTask 的工作原理,我们从它的 execute 方法开始分析,execute 方法有会调用 executeOnExecutor 方法,它们的实现如下所示:

@MainThreadpublic final AsyncTask execute(Params... params) {    return executeOnExecutor(sDefaultExecutor, params);}@MainThreadpublic final AsyncTask executeOnExecutor(Executor exec, Params... params) {    if (mStatus != Status.PENDING) { // 1        switch (mStatus) {            case RUNNING: throw new IllegalStateException(...);            case FINISHED: throw new IllegalStateException(...);        }    }    mStatus = Status.RUNNING;    onPreExecute(); // 2    mWorker.mParams = params; // 3    exec.execute(mFuture); // 4    return this;}

当我们调用AsyncTask的execute方法的时候会立即调用它的executeOnExecutor方法,这里传入了两个参数,分别是一个Executor和任务的参数params。在1处,会对AsyncTask当前的状态进行判断,这就对应了前面说的,一个任务只能被执行一次。在2处会调用onPreExecute方法,如果我们覆写了该方法,那么它就会在这个时候被调用。3处的操作是在为mWorker赋值,即把调用execute方法时传入的参数赋值给了它。mWorker是一个WorkerRunnable类型的实例,而WorkerRunnable又继承自Callable。在AsyncTask的构造方法中会创建并初始化它,并将其包装成一个FutureTask类型的字段,即mFuture。而在4处,我们就将使用传入的Executor来执行该mFuture。以下代码是mWorker和mFuture的相关内容:

mWorker = new WorkerRunnable() {    public Result call() throws Exception {        // 原子的布尔类型,设置为true标记任务为已经开始的状态        mTaskInvoked.set(true);        Result result = null;        try {            // 我们用来执行后台逻辑的方法会在这里被回调            result = doInBackground(mParams);        } catch (Throwable tr) {            // 也是原子布尔类型的引用,用来标记任务为已经取消的状态            mCancelled.set(true);            throw tr;        } finally {            // 发送执行的结果            postResult(result);        }        return result;    }};// 将上面的mWorker包装成FutureTask对象mFuture = new FutureTask(mWorker) {...};

在上面的代码中,sDefaultExecutor 实际是一个串行的线程池,一个进程中所有的 AsyncTask 全部在这个串行的线程池中排队执行,这个排队执行的过程后面会在进行分析。在 executeOnExecutor 方法中,AsynTask 的 onPreExecute 方法最先执行,然后线程池开始执行。下面分析线程池的执行过程如下:

ublic static final Executor SERIAL_EXECUTOR = new SerialExecutor();private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;private static class SerialExecutor implements Executor {    final ArrayDeque mTasks = new ArrayDeque();    Runnable mActive;    public synchronized void execute(final Runnable r) {        mTasks.offer(new Runnable() {            public void run() {                try {                    // 相当于对传入的Runnable进行了一层包装                    r.run();                } finally {                    // 分配下一个任务                    scheduleNext();                }            }        });        // 如果当前没有正在执行的任务,那么就尝试从队列中取出并执行        if (mActive == null) {            scheduleNext();        }    }    protected synchronized void scheduleNext() {        // 从队列中取任务并使用THREAD_POOL_EXECUTOR执行        if ((mActive = mTasks.poll()) != null) {            THREAD_POOL_EXECUTOR.execute(mActive);        }    }}

注意以下这里的sDefaultExecutor是一个静态变量,默认情况下,AsyncTask的所有的实例都会放在该线程池当中被依次、串行地调度。它通过内部维护的双端队列,每当一个AsyncTask调用execute方法的时候都会被放在该队列当中进行排队。如果当前没有正在执行的任务,那么就从队列中取一个任务交给THREAD_POOL_EXECUTOR执行,当一个任务执行完毕之后又会调用scheduleNext取下一个任务执行。也就是说,实际上sDefaultExecutor在这里只是起了一个任务调度的作用,而任务最终还是交给THREAD_POOL_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;}

上面的WorkerRunnable中已经用到了postResult方法,它用来将任务执行的结果发送给Handler。

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

这里通过mHandler发送信息。mHandler会在创建AsyncTask的时候被创建,当传入的Looper为null的时候,会使用AsyncTask内部的InternalHandler创建Handler:

private final Handler mHandler;public AsyncTask(@Nullable Looper callbackLooper) {    // 根据传入的参数创建Handler对象    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()         ? getMainHandler() : new Handler(callbackLooper);}private static Handler getMainHandler() {    synchronized (AsyncTask.class) {        if (sHandler == null) {            // 使用InternalHandler创建对象            sHandler = new InternalHandler(Looper.getMainLooper());        }        return sHandler;    }}// AsyncTask内部定义的Handler类型private static class InternalHandler extends Handler {    public InternalHandler(Looper looper) {        super(looper);    }    @Override public void handleMessage(Message msg) {        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;        switch (msg.what) {            case MESSAGE_POST_RESULT: result.mTask.finish(result.mData[0]); break;            case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break;        }    }}

可以发现,sHandler 是一个静态的 Handler 对象,为了能够将执行环境切换到主线程,这就要求 sHandler 这个对象必须在主线程中创建。由于静态成员会在加载类的时候进行初始化,因此这就变相要求 AsyncTask 的类必须在主线程中国你加载,否则同一个进程中的AsyncTask 都将无法工作。 sHandler 收到 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 方法,可以看到 doInBackground 的返回结构会传递给 onPostExecute 方法。

3、HandlerThread

HandlerThread 继承了 Thread,它是一种可以使用 Handler 的 Thread,它的实现也很简单,就是在 run 方法中通过 Looper.prepare() 来创建消息队列,并通过 Looper.loop() 来开启消息循环,这样在实际的使用中就允许在 HandlerThread 中创建 Handler 了。HandlerThread 中 run 方法如下所以:

  @Override    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,IntentService 稍后介绍。由于 HandlerThread 的 run 方法是一个无限循环,因此当明确不需要再使用 HandlerThread 时,可以通过它的 quit 或者 quitSfely 方法终止线程的执行。

4、IntentService

IntentService 是一个特殊的 Service,它继承了 Service 并且它是一个抽象类,因此必须创建它的子类才能使用 IntentService。IntentService 可用于执行后台耗时的任务,当任务执行后它会自动停止,同时由于 IntentService 是服务的原因,这导致它的优先级比单纯的线程要高的多,所以 IntentService 比较适合执行一些高优先级的后台任务,因为它优先级高不容易被系统杀死。在实现上,IntentService 封装了 HandlerThread 和 Handler,这一点可以从它的 onCreate 方法中看出来,如下所示:

    @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();        mServiceHandle = new ServiceHandle(mServiceLooper);    }

当 IntentService 被第一次启动时,它的 onCreate 方法会被调用,onCreate 方法会创建一个 HandlerThread,然后使用它的 Looper 来创建一个 HandleThread,然后使用它的 Looper 来构造一个 Handle 对象 mServiceHandle,这样通过 mServiceHandle 发送的消息最终都会在 HandleThread 中,从这个角度来看,IntentService 也可以用于执行后台任务。每次启动 IntentService,它的 onStartCommand 方法就会调用一次,IntentService 在 onStartCommand 中处理每个后台任务的 Intent。下面看一下 onStartCommand 方法是如何处理外界的 Intent 的, onStartCommand 调用了 onStart,onStart 方法的实现如下:

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

可以看出,IntentService 仅仅是通过 mServiceHandle 发送了一个消息,这个消息会在 HandlerThread 中被处理。mServiceHandle 收到消息后,会将 Intent 对象传递给 onHandleIntent 中被处理。 mServiceHandler 收到消息后,会将 Intent 对象传递给 onHandleIntent 方法去处理。注意这个 Intent 对象的内容和外界的 startService(intent) 中的 intent 的内容是完全一致的,通过这个这个 Intent 对象即可以解析出外界启动 IntentService 时所传递的参数,通过这些参数就可以区分具体的后台任务,这样在 onHandleIntent 方法就可以对不同的后台任务做处理了。当 onHandleIntent 方法执行介绍后,IntentService 会通过 sopSelf(int startId) 方法来尝试停止服务。这里之所以采用 stopSelf(int startId) 而不是 stopSelf() 来停止服务,那是因为 stopSelf() 会立即停止服务,而这个时候可能还有其他的消息未处理,stopSelf(int startId) 则会等待所有的消息都处理完毕才终止服务。一般来说,stopSelf(int startId) 在尝试停止服务之前会判断最近启动服务的次数是否和 startId 相等,如果相等就立刻停止服务,不相等则不停止服务,这个策略可以从 AMS 的 stopServiceToken 方法的实现中找到依据。ServiceHandler 的实现如下:

private final calss 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 方法执行完这个任务后,stopSelf(int startId) 就会直接停止服务;如果存在多个后台任务,那么当 onHandleIntent 方法执行完最后一个任务后,stopSelf(int startId)才会停止服务。另外,由于没执行一个后台任务就必须启动一次 IntentService,而 IntentService 内部则通过消息的方式向 HandlerThread 请求执行任务,Handler 中 Looper 是顺序处理消息的,这就意味着 IntentService 也是顺序执行后台任务的,当有多个后台任务同时存在时,这些后台任务会按照外界发起的顺序排队执行。

三、Android 中的线程池

线程池的有的主要如下三点:
(1)、重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
(2)、能有效的控制线程池的最大并发数,避免大量的线程之间因为相互抢占系统资源而导致的阻塞现象。
(3)、能够对线程进行简单的管理,并提供定时执行已经指定间隔循环执行等功能。

Android 中线程池的概念来源于 Java 中的 Executor,Executor 是一个接口,真正的线程池的实现为 ThreadPoolExecutor。ThreadPoolExecutor 提供了一系列参数来配置线程池,通过不同的参数可以创建不同的线程池,从线程池的功能特性来说,Android 的线程池主要分为 4 类,这 4 类线程池可以通过 Executors 所提供的工厂方法来得到,由于 Android 中的线程池都是都是直接或者间接的通过配置 ThreadPoolExecutor 来实现的,因为在介绍它们之前先介绍 ThreadPoolExecutor。

1、ThreadPoolExecutor

ThreadPoolExecutor 是线程池的真正实现,它的构造方法提供了一系列参数来配置线程池,下面介绍 ThreadPoolExecutor 的构造方法中各个参数的含义,这些参数直接会影响线程池的功能特性,下面是 ThreadPoolExecutor 的一个比较常用的构造方法:

public ThreadPoolExecutor(int corePoolSize,                          int maximumPoolSize,                          long keepAliveTime,                          TimeUnit unit,                          BlockingQueue workQueue,                          ThreadFactory threadFactory,                          RejectedExecutionHandler handler)
  • corePoolSize
    线程池的核心线程数,默认情况下,核心线程会在线程中一直存活,即使处于闲置状态。如果将 ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为 true,那么闲置的核心线程则在等待新任务到来时会有超时策略,这个时间间隔由 keepAliveTime 所指定,当等待时间超过 keepAliveTime 所指定的时长之后,核心线程就会被终止。

  • maximumPoolSize
    线程池所能容纳的最大的线程数,当活动线程数达到这个数值之后,后续的新任务就会被阻塞。

  • keepAliveTime
    非核心线程闲置的超时时长,超过这个时长,非核心线程就会被回收。当 ThreadPoolExecutor 的 allowCoreThreadOut 属性设置为 true 时,keepAliveTime 同样用于核心线程。

  • unit
    用于指定 keepAliveTime 参数的时间单位,这是一个枚举,常用的有 TimeUnit.MILLISECONDS(毫秒),TImeUnit.SECONDS(秒)以及 TimeUnit.MINUTES(分钟) 等。

  • workQueue
    线程池中的任务队列,通过线程池的 execute 方法提交的 Runnable 对象存储在这个参数中。

  • threadFactory
    线程工厂,为线程提供创建新线程的功能。ThreadFactory 是一个接口,只有一个方法:Thread newThread(Runnable r)。

除了上面的这些主要参数之外,ThreadPoolExecutor 还有一个不常用的参数 RejectedExecutionHandler handler。当线程池无法执行新任务时,这可能是由于任务队列已满或者是无法成功执行任务,这个时候 ThreadPoolExecutor 会调用 handler 的 rejectedExecution 方法来通知调用者,默认情况下 rejectedExecution 方法会直接抛出一个 RejectedExecutionException。 ThreadPoolExecutor 为 RejectedExecutionHandler 提供了几个可选值: CallerRunsPolicy,AbortPolicy,DiscardPolicy 和 DiscardOldestPolicy,其中 AbortPolicy 是 默认值,它会直接抛出 RejectedExecutionException,由于 handler 这个参数不常用,这里不在分析。

ThreadPoolExecutor 执行任务时大致遵循如下规则:
(1)、如果线程池中的线程数量为达到核心线程数量,那么会直接启动一个核心线程来执行任务。
(2)、如果线程池中的线程数量已经达到或者超过核心线程数量,那么新任务会被插入到任务队列中排队等待执行。
(3)、如果在步骤(2) 中无法插入到任务队列,往往由于任务队列已满,这个时候如果新线程数量未达到线程池规定的最大值,那么立刻启动一个非核心线程来执行任务。
(4)、如果步骤(3) 中线程数量已经达到核心线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor 会调用 RejectedExecutionHandler 的 rejectedExecution 方法来通知调用者。

ThreadPoolExecutor的参数配置在AsyncTask中有明显的体现,下面是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 workprivate 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 sPoolWorkQueue =        new LinkedBlockingQueue(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;}

从上面的代码可以知道,AsyncTask对THREAD_POOL_EXECUTOR这个线程池进行了配置,配置后的线程池规格如下:

  • 核心线程数等于CPU核心数+1
  • 线程池的最大线程数为CPU核心数的2倍+1
  • 核心线程无超时机制,非核心线程在闲置时的超时时间为1秒
  • 任务队列的容量为128
2、线程池的分类

本节将接着介绍Android中最常见的四类具有不同功能特性的线程池,它们都直接或间接地通过配置ThreadPoolExecutor来实现自己的功能特性,这四类线程池分别是FixedThreadPool,CachedThreadPool,,ScheduledThreadPool以及SingleThreadExecutor。

  • FixedThreadPool
    过Executors的newFixedThreadPool方法来创建,它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这意味着它能更加快速地响应外界的请求。newFixedThreadPool方法的实现如下,可以发现FixedThreadPool中只有核心线程并且这些核心线程没有超时机制,另外任务队列也是没有大小限制的
public static ExecutorService newFixedThreadPool(int nThreads) {    return new ThreadPoolExecutor(nThreads, nThreads,                                  0L, TimeUnit.MILLISECONDS,                                  new LinkedBlockingQueue());}
  • 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());}
  • ScheduledThreadPool
    通过Executors的newScheduledThreadPool方法来创建。它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收。ScheduledThreadPool这类线程池主要用于执行定时任务和具有固定周期的重复任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {    return new ScheduledThreadPoolExecutor(corePoolSize);}public ScheduledThreadPoolExecutor(int corePoolSize) {    super(corePoolSize, Integer.MAX_VALUE,          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,          new DelayedWorkQueue());}
  • SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {    return new FinalizableDelegatedExecutorService        (new ThreadPoolExecutor(1, 1,                                0L, TimeUnit.MILLISECONDS,                                new LinkedBlockingQueue()));}

除了上面系统提供的4类线程池以外,也可以根据实际需要灵活地配置线程池。下面的代码演示了系统与之的4中线程池的典型使用方法。

Runnable common = new Runnable() {    @Override    public void run() {        SystemClock.sleep(2000);    }};ExecutorService fix = Executors.newFixedThreadPool(4);fix.execute(common);ExecutorService cache = Executors.newCachedThreadPool();fix.execute(common);ScheduledExecutorService schedu = Executors.newScheduledThreadPool(4);schedu.schedule(common,2000,TimeUnit.MICROSECONDS);schedu.scheduleAtFixedRate(common,10,1000,TimeUnit.MICROSECONDS);ExecutorService single = Executors.newSingleThreadExecutor();single.execute(common);



《 Android 开发艺术探索》 Android 的线程和线程池

更多相关文章

  1. Android Service的使用方法 音乐播放器实例
  2. Android定制RadioButton样式三种实现方法
  3. android调用Webservice方法
  4. Android 文件读写操作方法总结
  5. 低版本android project在高版本ADK中运行方法
  6. Android中应用界面主题Theme使用方法和页面定时跳转应用
  7. G1安装android软件方法 (实机运行)
  8. android设置主题和自定义主题的方法

随机推荐

  1. android 悬浮窗教程
  2. Android让帧动画在结束时消失
  3. Hack4-自定义PreferenceActivity界面
  4. Android图片缩放总结及比较
  5. Android launcher -- launcher源码修改 1
  6. Android调试高德SDK,如何获取SHA1?
  7. Android 使用模拟位置(支持Android 6.0)
  8. Android(安卓)Studio编译jar架包必看
  9. Android一次刷机
  10. android AIDL通信