> 文档中心 > 《Android开发艺术探索》第11章-Android 的线程和线程池读书笔记

《Android开发艺术探索》第11章-Android 的线程和线程池读书笔记

目录

  • 1. 前言
  • 2. 正文
    • 2.1 主线程和子线程
      • 2.1.1 在 Android 中,可以扮演线程角色的类有哪些?
      • 2.1.2 线程池的好处是什么?
      • 2.1.3 Android 中主线程和子线程的职责分别是什么?
    • 2.2 Android 中的线程形态
      • 2.2.1 AsyncTask 实际开发中如何使用?
      • 2.2.2 AsyncTask 的两个操作方法,三个泛型参数和五个核心方法分别是什么?
      • 2.2.3 AsyncTask 在使用过程中有哪些限制?
      • 2.2.4 AsyncTask 的类加载是在哪里完成的?会完成哪些工作?
      • 2.2.5 AsyncTask 的工作原理是什么?
      • 2.2.6 AsyncTask 内存泄漏是怎么产生的?如何解决?
      • 2.2.7 如何让 AsyncTask 并行执行任务呢?
      • 2.2.8 HandlerThread 的工作过程是什么?
      • 2.2.9 IntentService 和 Service 的区别是什么?
      • 2.2.10 IntentService 的工作过程是什么?
      • 2.2.11 IntentService 是如何保证多个异步任务的顺序执行的?
      • 2.2.12 从源码上找出 Service 的 stopSelf() 和 stopSelf(int startId) 的区别在哪里?
    • 2.3 Android 中的线程池
      • 2.3.1 ThreadPoolExecutor 的构造方法中各个参数的含义是什么?
      • 2.3.2 ThreadPoolExecutor 执行任务的规则是什么?
      • 2.3.3 ThreadPoolExecutor 的 UML 类图是什么?
      • 2.3.4 Executors 里的线程池有哪些?这些线程池有什么特点?
  • 3. 最后
  • 参考

1. 前言

本文主要对 Android 的线程和线程池部分进行总结。

2. 正文

2.1 主线程和子线程

2.1.1 在 Android 中,可以扮演线程角色的类有哪些?

ThreadAsyncTaskHandlerThreadIntentService。在 Jetpack 中,新增加的 WorkManager

描述 用途
AysncTask 封装了线程池和 Handler 为了方便开发者处理异步任务,并在 UI 线程更新数据。
HandlerThread 是一种具有消息循环的线程,在它的内部使用了 Handler 方便构建具有消息循环的线程。
IntentService 是一个服务,内部采用 HandlerThread 来执行异步任务,当任务执行完毕后 IntentService 会自动退出。 可以方便地执行异步任务,结合了线程(异步)和服务(不容易被系统杀死)。

2.1.2 线程池的好处是什么?

线程的特点:

  • 线程是一种受限的系统资源,即线程不可能无限制地产生;

  • 线程的创建和销毁都会有相应的开销;

  • 线程的调度是系统通过时间片轮转的方式来进行,这也有一定的开销,线程很多时,开销更大。

线程池可以缓存一定数量的线程,避免因为频繁创建和销毁线程带来的系统开销。

  • 重用线程池中的线程,避免因为线程的创建和销毁所带来的的性能开销;
  • 能够有效地控制线程池的最大并发数,避免大量的线程间因相互抢占系统资源而导致的阻塞现象;
  • 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行。

2.1.3 Android 中主线程和子线程的职责分别是什么?

主线程主要负责界面交互的逻辑,在任何时候必须保证有较高的响应速度,否则界面会有卡顿的感觉,主线程不能处理耗时任务;

子线程也叫工作线程,除了主线程以外的线程都是子线程,主要负责处理耗时操作。

如果在主线程执行了耗时操作,如进行网络访问,从 Android3.0 开始网络访问会失败并抛出 NetworkOnMainThreadException 异常;进行I/O操作或者其他耗时操作,主线程会因超过响应时间限制从而出现 ANR 现象。

2.2 Android 中的线程形态

2.2.1 AsyncTask 实际开发中如何使用?

public class AsyncTaskActivity extends Activity {    private static final String TAG = AsyncTaskActivity.class.getSimpleName();    private static final String url = "https://github.com/jhwsx/android-art-research/raw/"     + "f6257e9f1e46848400f7ff2635991fd5a850d4f8/chapter_11/app-debug.apk";    TextView mTvProgress;    TextView mTvLength;    private DownloadTask mDownloadTask;    @Override    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_asynctask); Button btnStartDownload = (Button) findViewById(R.id.btn_start_download); Button btnCancelDownload = (Button) findViewById(R.id.btn_cancel_download); mTvProgress = (TextView) findViewById(R.id.tv_download_progress); mTvLength = (TextView) findViewById(R.id.tv_download_length); btnStartDownload.setOnClickListener(new View.OnClickListener() {     @Override     public void onClick(View v) {  mDownloadTask = new DownloadTask(AsyncTaskActivity.this);  mDownloadTask.execute(url);     } }); btnCancelDownload.setOnClickListener(new View.OnClickListener() {     @Override     public void onClick(View v) {  mDownloadTask.cancel(false);     } });    }    public static class DownloadTask extends AsyncTask<String, Integer, Long> { private WeakReference<AsyncTaskActivity> weakReference; private ProgressDialog mProgressDialog; public DownloadTask(AsyncTaskActivity activity) {     weakReference = new WeakReference<>(activity); } @Override protected void onPreExecute() {     super.onPreExecute();     mProgressDialog = new ProgressDialog(weakReference.get());     mProgressDialog.setTitle("下载");     mProgressDialog.setMessage("获取数据中...");     mProgressDialog.show();     Log.d(TAG, "onPreExecute: threadName=" + Thread.currentThread().getName()); } @Override protected void onProgressUpdate(Integer... values) {     super.onProgressUpdate(values);     if (mProgressDialog != null && mProgressDialog.isShowing()) {  mProgressDialog.dismiss();  mProgressDialog = null;     }     Log.d(TAG, "onProgressUpdate: progress=" + values[0] + ", threadName=" + Thread.currentThread().getName());     AsyncTaskActivity asyncTaskActivity = weakReference.get();     if (asyncTaskActivity != null) {  asyncTaskActivity.mTvProgress.setText("下载进度: " + values[0] + "%");     } } @Override protected Long doInBackground(String... urls) {     Log.d(TAG, "doInBackground: threadName=" + Thread.currentThread().getName());     HttpURLConnection connection = null;     InputStream inputStream = null;     OutputStream outputStream = null;     long contentLength = -1;     try {  URL url = new URL(urls[0]);  connection = (HttpURLConnection) url.openConnection();  connection.setConnectTimeout(30 * 1000);  connection.setReadTimeout(30 * 1000);  connection.connect();  if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {      return -1L;  }  contentLength = connection.getContentLength();  inputStream = connection.getInputStream();  outputStream = new FileOutputStream(new File(weakReference.get().getExternalFilesDir(null), "download.apk"));  byte[] buffer = new byte[4 * 1024];  int total = 0;  int length;  while ((length = inputStream.read(buffer)) != -1) {      total += length;      final int progress = (int) (total * 100f / contentLength);      if (isCancelled()) {   break;      }      publishProgress(progress);      outputStream.write(buffer, 0, length);  }     } catch (Exception e) {  e.printStackTrace();     } finally {  CloseUtils.closeIOQuietly(inputStream, outputStream);  if (connection != null) {      connection.disconnect();  }     }     return contentLength; } @Override protected void onPostExecute(Long aLong) {     super.onPostExecute(aLong);     Log.d(TAG, "onPostExecute: threadName=" + Thread.currentThread().getName());     AsyncTaskActivity asyncTaskActivity = weakReference.get();     if (asyncTaskActivity != null) {  asyncTaskActivity.mTvLength.setText("下载字节数: " + aLong);  asyncTaskActivity.mTvLength.setVisibility(View.VISIBLE);     } } @Override protected void onCancelled() {     super.onCancelled();     Log.d(TAG, "onCancelled: " + Thread.currentThread().getName());     AsyncTaskActivity asyncTaskActivity = weakReference.get();     if (asyncTaskActivity != null) {  asyncTaskActivity.mTvLength.setVisibility(View.VISIBLE);  asyncTaskActivity.mTvLength.setText("任务已取消");     } }    }}

点击开始下载按钮,打印日志如下:

D/AsyncTaskActivity: onPreExecute: threadName=mainD/AsyncTaskActivity: doInBackground: threadName=AsyncTask #1D/AsyncTaskActivity: onProgressUpdate: progress=0, threadName=mainD/AsyncTaskActivity: onProgressUpdate: progress=1, threadName=mainD/AsyncTaskActivity: onProgressUpdate: progress=2, threadName=main...D/AsyncTaskActivity: onProgressUpdate: progress=98, threadName=mainD/AsyncTaskActivity: onProgressUpdate: progress=100, threadName=mainD/AsyncTaskActivity: onPostExecute: threadName=main

2.2.2 AsyncTask 的两个操作方法,三个泛型参数和五个核心方法分别是什么?

AsyncTask 是一个抽象的泛型类,它提供了 ParamsProgressResult 这三个泛型参数。其中 Params 表示异步任务的输入参数的类型,如下载链接等,Progress 表示异步任务的执行进度的类型,比如进度的百分数,Result 表示异步任务的返回结果的类型,如下载文件的长度。如果确实不需要某个具体的参数,可以传入 Void 来代替。

五个核心方法:

方法 执行线程 作用
onPreExecute() 主线程 在异步任务执行之前,回调此方法,一般可以做一些准备工作。
Result doInBackground(Params... params) 线程池 用于执行异步任务,params 参数表示异步任务的输入参数,返回值表示要返回 onPostExecute 方法的结果。另外,在这个方法里,通过 publishProgress(Progress... values) 方法来更新异步任务的进度,最终会调用 onProgressUpdate 方法。
onProgressUpdate(Progress... values) 主线程 当异步任务的执行进度发生改变时会回调这个方法。
onPostExecute(Result result) 主线程 在异步任务执行完毕后,会回调此方法,接收 doInBackground 的返回值。
onCancelled(Result result) 主线程 当异步任务被取消时,会回调此方法,此时不会回调 onPostExecute

两个操作方法:

方法 调用线程 作用
AsyncTask execute(Params... params) 主线程 开始执行异步任务
boolean cancel(boolean mayInterruptIfRunning) 没有限制 取消异步任务的执行

2.2.3 AsyncTask 在使用过程中有哪些限制?

  1. AsyncTask 的类必须在主线程中加载,这主要是为了保证 sHandler 的创建是在主线程,这一点 android22 之后的 AsyncTask 已发生了变化;
  2. execute() 方法在主线程调用,这主要是为了保证 onPreExecute 方法在主线程调用;
  3. 不要在程序中直接调用 onPreExecute()onPostExecute()doInBackgroundonProgressUpdate 方法;
  4. 一个 AsyncTask 对象只能调用一次 execute 方法,多次调用会报运行时异常;
  5. 在 Android1.6 之前,AsyncTask 是串行执行任务的,Android1.6 至 Android2.3,AsyncTask 采用线程池里处理并行任务,Android3.0 开始,AsyncTask 采用一个线程来串行执行任务。

这些限制的原因会在本文的后面逐个进行说明的。

2.2.4 AsyncTask 的类加载是在哪里完成的?会完成哪些工作?

AsyncTask 的类加载在 Android4.1及以上已经被系统自动完成,具体是在 ActivityThreadmain 方法中,调用 AsyncTaskinit 方法来完成的。

public static void main(String[] args) {    // 创建主消息循环的 Looper 对象    Looper.prepareMainLooper();    ActivityThread thread = new ActivityThread();    thread.attach(false);    if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler();    }    // 初始化 AsyncTask    AsyncTask.init();    // 开启主消息循环    Looper.loop();    throw new RuntimeException("Main thread loop unexpectedly exited");}

我们知道,静态成员在加载类的时候会进行初始化,所以我们还应该关注 AsyncTask 有哪些静态成员被初始化了。

public abstract class AsyncTask<Params, Progress, Result> {    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);    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 final InternalHandler sHandler = new InternalHandler();    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;    public static void init() { sHandler.getLooper();    }    private static class InternalHandler extends Handler {    }}

可以看到,有两个线程池(THREAD_POOL_EXECUTORSERIAL_EXECUTOR)和一个 Handler sHandler 被初始化了。

需要说明的是,sHander 这个对象是在主线程创建的,这样在工作线程中才能通过 sHander 将数据从工作线程切换到主线程中。

2.2.5 AsyncTask 的工作原理是什么?

AsyncTask 的构造

public abstract class AsyncTask<Params, Progress, Result> {    private final WorkerRunnable<Params, Result> mWorker;    private final FutureTask<Result> mFuture; public AsyncTask() { mWorker = new WorkerRunnable<Params, Result>() {     public Result call() throws Exception {  mTaskInvoked.set(true);  Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  return postResult(doInBackground(mParams));     } }; 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("", e.getCause());  } catch (CancellationException e) {      postResultIfNotInvoked(null);  }     } };    }    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams;    }}

FutureTask 类在开发中见得不是很多,这里看一下对应的类图:

在这里插入图片描述

可以看到,FutureTask 实现了 RunnableFuture 接口,而 RunnableFuture 接口继承了 Runnable 接口,所以 FutureTask 也实现了 Runnable接口,这样 FutureTask 对象就可以放在线程池中执行了。

FutureTask 封装的任务被执行时,线程池会调用它的 run 方法,而 run 方法内部会调用 Callable 对象的 call 方法。

在这里,mFuture 对象会被放入线程池中执行,线程池会调用它的 run 方法,run 方法内部会调用 mWorker 对象的 call 方法,在 call 方法执行异步任务。

这里绘制一下它们之间的包含关系图,方便理解:

在这里插入图片描述

异步任务的提交

调用 execute 方法开始执行任务,内部会调用 executeOnExecutor 方法:

public abstract class AsyncTask<Params, Progress, Result> {public final AsyncTask<Params, Progress, Result> execute(Params... params) {return executeOnExecutor(sDefaultExecutor, params);}public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) { // 如果任务状态不是 PENDING 状态,都会抛出异常。if (mStatus != Status.PENDING) {switch (mStatus) {case RUNNING:throw new IllegalStateException("");case FINISHED:throw new IllegalStateException("");}} // 当前任务状态是 PENDING 状态,把任务状态更新为 RUNNING 状态。mStatus = Status.RUNNING; // 调用此方法,做执行任务前的一些准备工作。onPreExecute(); // 把参数数组赋值给 mWorker 的 成员变量 mParamsmWorker.mParams = params; // 把异步任务提交到线程池里面exec.execute(mFuture); // 返回 AsyncTask 对象,便于外部拿到它来取消,发布进度,判断是否取消等操作。return this;}public enum Status {/ * 任务还没有执行 */PENDING,/ * 任务正在运行 */RUNNING,/ * 任务结束了,包括执行完毕或者被取消 */FINISHED,}}

通过 mStatus 的控制,来保证一个 AsyncTask 对象只能调用一次 execute 方法,否则就抛出运行异常。

调用 execute 方法,实际上是把异步任务提交给默认的线程池来执行,这是一个串行的线程池。那么,这里是如何保证串行的呢?可以看下面的源码:

// 可以并行执行任务的线程池public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,  TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);// 一次执行一个任务,即串行执行任务的线程池// 需要注意的是 SERIAL_EXECUTOR 是进程唯一的。public static final Executor SERIAL_EXECUTOR = new SerialExecutor();private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;private static class SerialExecutor implements Executor {    // 用来存放即将要执行的异步任务,因为 SERIAL_EXECUTOR 是进程唯一的,所以 mTask 也是进程唯一的。    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();    // 记录是否有在执行中的异步任务    Runnable mActive;    // 加内置锁,保证了线程安全    public synchronized void execute(final Runnable r) { // 新的任务到来时,都先加到队列的尾部。 mTasks.offer(new Runnable() {     public void run() {  try {      r.run();  } finally {      // 当一个任务执行完毕后,安排队列中的下一个任务执行。      scheduleNext();  }     } }); // 如果没有在执行的异步任务,从队列中取任务执行。 if (mActive == null) {     scheduleNext(); }    }    protected synchronized void scheduleNext() { // 从队列的头部取出一个任务,赋值给 mActive,如果不为空,则放入线程池中执行。 if ((mActive = mTasks.poll()) != null) {     THREAD_POOL_EXECUTOR.execute(mActive); }    }}

因为 SERIAL_EXECUTOR 是进程唯一的,而默认情况下就是使用 SERIAL_EXECUTOR 作为线程池,所以一个进程中所有的 AsyncTask 都会在这个串行的线程池中排队执行。

SerialExecutor类并不是在 AsyncTask 中才有的,实际上,它是来自 Java 的 Executor 文档里面的。它通过使用 ArrayDeque 这个数组双端队列把任务调度的并行方式改为了串行方式。

这算得是静态代理模式的应用了,Executor 是公共的接口,SerialExecutor 是一个代理类,ThreadPoolExecutor 是真正的实体,也就是被代理类。

需要说明的是,exec.execute(mFuture); 只是把异步任务提交给了线程池而已,并不意味着异步任务会马上执行。这个方法的注释可以证明这一点:Executes the given task sometime in the future.

异步任务的执行

前面我们知道异步任务的提交已经完成了,那么异步任务的执行是在哪里呢?

异步任务的执行当然是在线程池里面,具体来说式在 ThreadPoolExecutorrunWorker 去执行异步任务,内部会调用 mFutureTask 对象(它是一个 Runnable 对象)的 run 方法,run 方法内部会调用 mWorker 对象(它是一个 Callable 对象)的 call 方法,因为我们知道 call 方法最终会在线程池中执行。

private final AtomicBoolean mTaskInvoked = new AtomicBoolean();mWorker = new WorkerRunnable<Params, Result>() {    public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); return postResult(doInBackground(mParams));    }};protected abstract Result doInBackground(Params... params);

call 方法中,主要做了三件事情:

  • mTaskInvoked 这个原子的布尔型成员变量的值设置为 true
  • 执行 AsyncTaskdoInBackground(Params... params),这个方法接收异步任务的输入参数(比如下载链接)并执行异步任务的代码(比如下载文件);
  • doInBackground 方法的返回值(比如下载文件的大小)传递给 postResult 方法。

异步任务的进度和结果

主线程需要获取异步任务的进度(如下载文件的百分比)和结果(如文件大小或者被取消了),以便在 UI 展示下载进度和下载结果。但是,异步任务是在线程池里面执行的,它的进度和结果目前都在子线程中。

那么,怎样把子线程的进度和结果数据切换到主线程呢?当然是要用到 Handler 了。

先看一下如何把子线程中异步任务的进度切换到主线程,需要在 doInBackground 方法中调用 publishProgress 方法

// sHandler 是 AsyncTask 类加载的时候初始化的,而 AsyncTask 类加载是在主线程,所以 sHandler 是在主线程创建的。private static final InternalHandler sHandler = new InternalHandler();protected final void publishProgress(Progress... values) {    if (!isCancelled()) { sHandler.obtainMessage(MESSAGE_POST_PROGRESS,  new AsyncTaskResult<Progress>(this, values)).sendToTarget();    }}

可以看到,在 isCancelled() 方法不为 true 时,也就是说异步任务没有取消时,先把 AsyncTask 对象和进度信息封装在 AsyncTaskResult 对象里,再把 AsyncTaskResult 对象封装在 Message 对象里面,最后通过 sHandlerMessage 对象发送到消息队列里面。

private static class AsyncTaskResult<Data> {    final AsyncTask mTask;    final Data[] mData;    AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data;    }}

其实这里使用 AsyncTaskResult 这个名字来表示进度信息很不准确,觉得叫 AsyncTaskProgress 还是很贴切的,对吧?

接着我们去看一下接收消息的地方,自然是在 InternalHandler 类重写的 handleMessage 方法里面:

private static class InternalHandler extends Handler {    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})    @Override    public void handleMessage(Message msg) { // 这个方法是在主线程执行的。 AsyncTaskResult result = (AsyncTaskResult) msg.obj; switch (msg.what) {     ...     case MESSAGE_POST_PROGRESS:  result.mTask.onProgressUpdate(result.mData);  break; }    }}

result.mTask 就是当前的 AsyncTask 对象,result.mData 就是要更新的进度值,它是一个数组。

result.mTask.onProgressUpdate(result.mData); 就是调用 AsyncTaskonProgressUpdate 方法,把进度值回调出去,这样就可以在重写的 AsyncTaskonProgressUpdate 方法里拿到进度值来更新页面上的进度值了。

异步任务的结果是通过 postResult 方法,来发送的:

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

AsyncTask 里面,会有两处调用 postResult 方法:

  • 在异步任务代码执行完毕后,即 doInBackground 方法执行完毕后,调用 postResult 方法来发送结果:

    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));    }};
  • 在调用 AsyncTaskcancel 方法后,最终会调用 FutureTaskdone 方法:

    public final boolean cancel(boolean mayInterruptIfRunning) {    // 把 mCancelled 的值设为 true,表示已经进行了取消操作    mCancelled.set(true);    return mFuture.cancel(mayInterruptIfRunning);}mFuture = new FutureTask<Result>(mWorker) {    @Override    protected void done() {  postResultIfNotInvoked(get());    }};private void postResultIfNotInvoked(Result result) {    final boolean wasTaskInvoked = mTaskInvoked.get();    // 如果任务还没有被调用,就发送结果。    if (!wasTaskInvoked) { postResult(result);    }}

接着看 postResult 方法,仍然是先把 AsyncTask 对象和结果信息封装在 AsyncTaskResult 对象里,再把 AsyncTaskResult 对象封装在 Message 对象里面,最后通过 sHandlerMessage 对象发送到消息队列里面。

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

接收结果的地方是在 InternalHandlerhandleMessage 方法里:

case MESSAGE_POST_RESULT:    // 在主线程里调用    result.mTask.finish(result.mData[0]);    break;
private void finish(Result result) {    if (isCancelled()) { onCancelled(result);    } else { onPostExecute(result);    }    mStatus = Status.FINISHED;}

可以看到 finish 方法里面,会先通过 isCancelled 判断有没有进行取消操作;如果已取消,就调用 onCancelled 方法,如果未取消,说明拿到了异步任务的结果,就调用 onPostExecute 方法;最后,把 mStatus 的值改为 FINISHED

从这里我们可以得出,对于一个 AsyncTask 对象来说,它的 onCancelled 方法和 onPostExecute 方法只会有一个会被调用;一个 AsyncTask 对象在执行完一次异步任务后,不能再次调用它的 execute 方法,这是因为它的 mStatusFINISHED 而不是 PENDING,再次调用 execute 方法会抛出运行异常。

2.2.6 AsyncTask 内存泄漏是怎么产生的?如何解决?

当在 Activity 中采用匿名类或者内部类的方式来创建 AsyncTask 时,AsyncTask 默认会持有外部类 Activity 的引用;如果在 Activity 关闭后,AsyncTask 的异步任务还没有执行完毕,那么 AsyncTask 对象就不会被释放,它所持有的 Activity 对象也不会被释放,导致 Activity 对象无法被及时回收,这就会造成内存泄漏。

《Android开发艺术探索》第11章-Android 的线程和线程池读书笔记

解决办法:使用静态内部类来子类化 AsyncTask,如果子类内部要使用到 Activity,采用弱引用的方式来引用 Activity 的实例;在 Activity 结束的时候,及时 cancelAsyncTask 任务。

2.2.7 如何让 AsyncTask 并行执行任务呢?

我们知道,AsyncTask 内部默认是串行执行任务的,那么有没有办法让它并行执行任务呢?有的。

使用 AsyncTaskexecuteOnExecutor 方法,来从外部指定线程池,避免使用内部默认的 SERIAL_EXECUTOR 串行线程池。

public void parallel_execute(View view) {    new MyAsyncTask("AsyncTask#1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");    new MyAsyncTask("AsyncTask#2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");    new MyAsyncTask("AsyncTask#3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");    new MyAsyncTask("AsyncTask#4").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");}

打印日志:

D/AsyncTaskActivity: AsyncTask#2 finished at 2022-01-29 15:27:28D/AsyncTaskActivity: AsyncTask#3 finished at 2022-01-29 15:27:28D/AsyncTaskActivity: AsyncTask#1 finished at 2022-01-29 15:27:28D/AsyncTaskActivity: AsyncTask#4 finished at 2022-01-29 15:27:28

可以看到,确实并行执行了。

2.2.8 HandlerThread 的工作过程是什么?

HandlerThread 用于创建一个拥有一个 Looper 对象的新线程。开发者可以使用 HandlerThreadLooper 对象去创建 Handler 对象。注意需要调用 start() 方法来开启 HandlerThread

HandlerThread 的源码分为三个部分来看:创建 Looper ,获取 Looper 和终止 Looper

创建 Looper

在调用 start 方法后,会执行 run 方法:

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

通过 Looper.prepare()方法来创建一个对应于线程的 Looper 对象;

进入同步代码块,通过 Looper.myLooper() 获取本线程的 Looper 对象,赋值给 HandlerThread 的成员变量 mLooper,调用 notifyAll() 唤醒其他等待锁的线程;

调用 onLooperPrepared 方法,表示 HandlerThread 已经准备好 Looper 对象了;

调用 Looper.loop() 方法开启消息循环。

获取 Looper

public Looper getLooper() {    if (!isAlive()) { return null;    }    // 如果 HandlerThread 线程已经开启,那么 Looper 对象没有创建好,就会一直等待。    synchronized (this) { while (isAlive() && mLooper == null) {     try {  wait();     } catch (InterruptedException e) {     } }    }    return mLooper;}

先判断 HandlerThread 线程对象是否存活,如果就不存活了,就直接返回 null

接着进入同步代码块,执行 wait() 操作,使用 while 来包裹它,循环条件是线程存活以及 mLoopernull,换句话说,如果线程死亡或者 mLooper不为 null,就不会处于等待了。

什么时候会有这样的场景呢?这里构建一个场景:

static class MyHandlerThread extends HandlerThread {    public MyHandlerThread(String name) { super(name);    }    @Override    public void run() { // 故意先休眠 3s,再执行父类的 run 方法 SystemClock.sleep(3000L); super.run();    }}public void handlerThread(View view) {    final HandlerThread handlerThread = new MyHandlerThread("WorkThread");    handlerThread.start();    // 开启 Thread1,在它的 run 方法里去获取 handlerThread 的 looper 对象。    new Thread(new Runnable() { @Override public void run() {     Log.d(TAG, "run: threadName=" + Thread.currentThread().getName());     Looper looper = handlerThread.getLooper();     Log.d(TAG, "run: threadName=" + Thread.currentThread().getName() + ",looper=" + looper); }    }, "Thread1").start();}

打印日志:

2022-01-29 08:30:21.650 D/MainActivity: run: threadName=Thread12022-01-29 08:30:24.653 D/MainActivity: run: threadName=Thread1,looper=Looper (WorkThread, tid 198) {2b5639a7}

当 Thread1 的 run 方法里调用 handlerThread.getLooper() 时,HandlerThread 类的 run 方法还未开始执行,所以 getLooper 方法里,Thread1 会获取锁对象,进入到同步代码块中:isAlive() && mLooper == null 条件成立,调用 Thread1 的 wait() 方法,Thread1 会进入等待状态并且释放锁对象。

过了 3s 后,HandlerThreadrun 方法得以执行,获取到锁对象,进入到同步代码块,把创建好的 Looper 对象赋值给 mLooper 成员变量,并调用 notifyAll() 唤醒同锁的等待线程,也就唤醒了 Thread1。

Thread1 被唤醒后,再次判断isAlive() && mLooper == null 条件,此时 mLooper 不为 null,所以循环条件不成立,退出同步代码块,直接返回 mLooper 对象。

终止 Looper

调用 HandlerThreadquit 方法或 quitSafely 方法来终止 Looper。内部调用了 LooperquitquitSafely 方法。这是为了在不使用消息循环时,终止消息循环,避免产生内存泄漏的问题。

使用 HandlerThread 的好处:

  • 可以安全方便地获取到 Looper 对象;
  • 可以方便地终止 Looper

在 Android 系统源码中,ActivityManagerServicePackageManagerServicePowerManagerService 都使用到了 HandlerThread,主要作用是把数据或者任务从主线程发送到工作线程去执行。

2.2.9 IntentService 和 Service 的区别是什么?

联系:IntentService 继承于 Service

区别:

  • IntentService 是一个抽象类,包含一个抽象方法 onHandleIntent,因此必须创建它的子类才能创建 IntentService,而 Service 是一个具体类。
  • IntentService 可以异步执行耗时的任务,当任务执行完毕后它会自动停止;而 Service 是在主线程运行的,不可以直接执行耗时任务,必须自己单独开启工作线程来执行耗时任务。

2.2.10 IntentService 的工作过程是什么?

子类化 IntentService

因为 IntentService 是一个包含抽象方法 onHandleIntent 的抽象类,所以子类需要实现 IntentService 的抽象方法 onHandleIntent 方法;

因为 IntentService 定义了一个带参构造器,就不再有无参构造器,所以子类需要显式地调用父类的带参构造器。

public class MyIntentService extends IntentService {    private static final String TAG = MyIntentService.class.getSimpleName();    public MyIntentService() { super(TAG);    }    @Overrideprotected void onHandleIntent(Intent intent) {    }}
public abstract class IntentService extends Service {    private String mName;    public IntentService(String name) { super(); mName = name;    }}

传给父类构造器的 name,会用于创建 HandlerThread 这个线程(不要忘记了,HandlerThread 是继承于 Thread 类的)对象的线程名字。

回调 IntentServiceonCreate() 方法

private volatile Looper mServiceLooper;private volatile ServiceHandler mServiceHandler;@Overridepublic void onCreate() {    super.onCreate();    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");    thread.start();    mServiceLooper = thread.getLooper();    mServiceHandler = new ServiceHandler(mServiceLooper);}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);    }}

可以看到,在 onCreate 方法中:

  • 创建并开启一个 HandlerThread 类型的线程,这是为了获取一个对应于这个线程的 Looper 对象;
  • 通过 HandlerThread 对象获取 Looper 对象,并赋值给 mServiceLooper 成员变量;
  • 使用 mServiceLooper 来创建一个 ServiceHandler 对象,并赋值给 mServiceHandler 成员变量。

需要注意的是,ServiceHandlerIntentService 的内部类,而不是静态内部类,这是因为在 handleMessage 中需要调用 Service 对象的 stopSelf(int startId) 方法。内部类默认会持有外部类的引用,而静态内部类并不会持有外部类的引用。

回调 IntentServiceonStartCommand() 方法

@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {    onStart(intent, startId);    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;}@Overridepublic void onStart(Intent intent, int startId) {    // 在主线程中发送消息    Message msg = mServiceHandler.obtainMessage();    msg.arg1 = startId;    msg.obj = intent;    mServiceHandler.sendMessage(msg);}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);    }}

应当知道,每调用一次 startService 方法,对应 ServiceonStartCommand 方法都会被调用一次。

内部调用了 onStart 方法,这是为了兼容处理的:onStart 属于废弃方法,onStartCommand 属于替代它的新方法;而把主要代码都放到 onStart 方法里面,就可以兼顾新旧版本了。

需要说明的是,Service 的生命周期方法都是在主线程调用的。因此,看 onStart 方法内部的代码,就是在主线程里面使用 mServiceHandler 发送一个消息(封装了 startIdintent)。那么,接收消息的地方在哪里呢?在 ServiceHandlerhandleMessage 方法里,这个方法是在子线程回调的。这样,就把消息(封装了 startIdintent)从主线程切换到了子线程中了。

handleMessage 方法中,通过 Message 对象取出 obj ,即 Intent 对象,取出 startId。这里的 Intent 对象的内容和 startService(Intent service) 中的 Intent 对象的内容是完全一致的。因此,在 onHandleIntent 方法中可以通过 Intent 对象解析出启动 Service 时所传递的参数,如下载链接,任务id等,来区分具体的异步任务。

onHandleIntent 方法执行完毕后,会接着执行 stopSelf(int startId) 方法,作用仅仅是尝试停止服务,也就是说,在满足条件时,就会真正地停止服务,在不满足条件时,就不会停止服务。那么,这个条件是什么?当最近启动服务的次数和 startId 相等,就会立即停止服务;不相等,就不停止服务。

2.2.11 IntentService 是如何保证多个异步任务的顺序执行的?

当多次调用 startService 方法,每次调用时让 IntentService 执行不同的异步任务,IntentService 内部会通过 Handler 把任务发送到子线程所持有的消息队列中,而子线程对应的 Looper 对象会顺序处理消息的,也就是说,在通过 onHandleIntent 方法处理完一个异步任务后,会从消息队列中取出下一个消息(封装了异步任务的参数),再通过 onHandleIntent 方法来处理对应的异步任务。这样就保证了,当多个异步任务都发送到 IntentService 时,这些异步任务的执行会按照发送的顺序一个一个地执行,而不会交叉,或者说并行。

这里通过代码实例来演示说明:

public class MyIntentService extends IntentService {    private static final String TAG = MyIntentService.class.getSimpleName();    public static final String EXTRA_TASK = "extra_task";    public MyIntentService() { super(TAG);    }    @Override    public void onCreate() { super.onCreate(); Log.d(TAG, "onCreate: ");    }    @Override    public void onStart(Intent intent, int startId) { Log.d(TAG, "onStart: startId = " + startId); super.onStart(intent, startId);    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand startId:" + startId); return super.onStartCommand(intent, flags, startId);    }    @Override    protected void onHandleIntent(Intent intent) { String task = intent.getStringExtra(EXTRA_TASK); Log.d(TAG, "onHandleIntent: " + task + " start"); long time; switch(task) {     case "task 0":  time = 4000; break;     case "task 1":  time = 2000; break;     case "task 2":  time = 5000; break;     case "task 3":  time = 1000; break;     case "task 4":  time = 3000; break;     default:  time = 0; break; } try {     Thread.sleep(time); } catch (InterruptedException e) {     e.printStackTrace(); } Log.d(TAG, "onHandleIntent: " + task + " finish");    }    @Override    public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy: ");    }}

MainActivity 中,点击按钮发送任务:

public void intentservice(View view) {    Intent service = new Intent(MainActivity.this, MyIntentService.class);    for (int i = 0; i < 5; i++) { service.putExtra(MyIntentService.EXTRA_TASK, "task " + i); Log.d(TAG, "startService: task " + i); startService(service);    }}

打印日志如下:

 14:17:51.013  D/MainActivity: startService: task 0 14:17:51.017  D/MainActivity: startService: task 1 14:17:51.020  D/MainActivity: startService: task 2 14:17:51.025  D/MainActivity: startService: task 3 14:17:51.027  D/MainActivity: startService: task 4 14:17:51.037  D/MyIntentService: onCreate:  14:17:51.037  D/MyIntentService: onStartCommand startId:1 14:17:51.037  D/MyIntentService: onStart: startId = 1 14:17:51.038  D/MyIntentService: onHandleIntent: task 0 start 14:17:51.041  D/MyIntentService: onStartCommand startId:2 14:17:51.041  D/MyIntentService: onStart: startId = 2 14:17:51.042  D/MyIntentService: onStartCommand startId:3 14:17:51.042  D/MyIntentService: onStart: startId = 3 14:17:51.042  D/MyIntentService: onStartCommand startId:4 14:17:51.042  D/MyIntentService: onStart: startId = 4 14:17:51.043  D/MyIntentService: onStartCommand startId:5 14:17:51.043  D/MyIntentService: onStart: startId = 5 14:17:55.041  D/MyIntentService: onHandleIntent: task 0 finish 14:17:55.043  D/MyIntentService: onHandleIntent: task 1 start 14:17:57.044  D/MyIntentService: onHandleIntent: task 1 finish 14:17:57.046  D/MyIntentService: onHandleIntent: task 2 start 14:18:02.049  D/MyIntentService: onHandleIntent: task 2 finish 14:18:02.050  D/MyIntentService: onHandleIntent: task 3 start 14:18:03.053  D/MyIntentService: onHandleIntent: task 3 finish 14:18:03.055  D/MyIntentService: onHandleIntent: task 4 start 14:18:06.058  D/MyIntentService: onHandleIntent: task 4 finish 14:18:06.064  D/MyIntentService: onDestroy: 

可以看到 14:17:51 的多行日志都是迅速打印的,这主要是发送任务相关的。

后面的日志是任务一个一个顺序执行的打印。

任务名字 任务需要执行的时间(毫秒) 任务执行开始时间 任务执行结束时间
task 0 4000 14:17:51.038 14:17:55.041
task 1 2000 14:17:55.043 14:17:57.044
task 2 5000 14:17:57.046 14:18:02.049
task 3 1000 14:18:02.050 14:18:03.053
task 4 3000 14:18:03.055 14:18:06.058

2.2.12 从源码上找出 Service 的 stopSelf() 和 stopSelf(int startId) 的区别在哪里?

public final void stopSelf() {    stopSelf(-1);}public final void stopSelf(int startId) {    if (mActivityManager == null) { return;    }    try { mActivityManager.stopServiceToken(  new ComponentName(this, mClassName), mToken, startId);    } catch (RemoteException ex) {    }}

可以看到:stopSelf() 方法仅仅是 stopSelf(-1) 的封装而已。

那么,startId 到底是什么含义呢?

根据 2.2.11 的日志:

 14:17:51.037  D/MyIntentService: onStartCommand startId:1 14:17:51.041  D/MyIntentService: onStartCommand startId:2 14:17:51.042  D/MyIntentService: onStartCommand startId:3 14:17:51.042  D/MyIntentService: onStartCommand startId:4 14:17:51.043  D/MyIntentService: onStartCommand startId:5

可以看到,调用 5 次 startServicestartId 的值从 1 增加到了 5。这是为什么呢?

在继续阅读下文之前,希望同学们了解 Service 的启动过程。如果不了解的话,可以参考笔者的文章:Android筑基——Service的启动过程之同进程启动(基于api21)。

lastStartId 的递增

ActiveServicesstartServiceLocked 方法里面:

// 在获取到 ServiceRecord r对象之后,会给它的 pendingStarts 添加一个元素。r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),  service, neededGrants));

查看 ServiceRecordmakeNextStartId 方法

public int makeNextStartId() {     lastStartId++;     if (lastStartId < 1) { lastStartId = 1;     }     return lastStartId;}

这个方法的作用:对 lastStartId 自增后并返回。具体而言:启动 1 次 MyIntentServicelastStartId 的值就是 1;启动 2 次 MyIntentServicelastStartId 的值就是 2。因此,lastStartId 的含义是最近启动服务的次数。

接收startId

sendServiceArgsLocked 方法中,会通过跨进程调用的方式,把 lastStartId 发送到 onStartCommand 方法的 startId 参数里面。

private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg, boolean oomAdjusted) {    // 遍历 ServiceRecord 的 pendingStarts 列表    while (r.pendingStarts.size() > 0) { try {     ServiceRecord.StartItem si = r.pendingStarts.remove(0);     r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent); } catch (RemoteException e) { }    }}

到这里,我们知道,onStartCommand 方法的startId 值就是当次的 ServiceRecordlastStartId 值。

AMS 的 stopServiceToken 方法

public boolean stopServiceToken(ComponentName className, IBinder token, int startId) {    synchronized(this) { return mServices.stopServiceTokenLocked(className, token, startId);    }}

ActiveServices 的 stopServiceTokenLocked 方法

boolean stopServiceTokenLocked(ComponentName className, IBinder token, int startId) {    ServiceRecord r = findServiceLocked(className, token, UserHandle.getCallingUserId());    if (r != null) { if (startId >= 0) {     // 如果最近启动服务的次数 和 startId 不相等,就退出方法执行。     if (r.getLastStartId() != startId) {  return false;     }      } // 去停止服务 bringDownServiceIfNeededLocked(r, false, false); Binder.restoreCallingIdentity(origId); return true;    }    return false;}

startId 的值为 -1 时,不会满足 startId >= 0 这一条件,所以会直接去调用停止服务;

startId 的值大于等于 0 时,只有 startId 的值等于最近启动服务的次数时,才会去调用停止服务。

2.3 Android 中的线程池

2.3.1 ThreadPoolExecutor 的构造方法中各个参数的含义是什么?

public ThreadPoolExecutor(int corePoolSize,// 第 1 个参数     int maximumPoolSize,// 第 2 个参数     long keepAliveTime,// 第 3 个参数     TimeUnit unit,// 第 4 个参数     BlockingQueue<Runnable> workQueue,// 第 5 个参数     ThreadFactory threadFactory,// 第 6 个参数     RejectedExecutionHandler handler) {   // 第 7 个参数
序号 参数名 参数类型 参数含义 取值范围 解释说明
1 corePoolSize int 核心线程数 >=0 如果等于 0,则任务执行完成后,没有任何请求进入时就销毁线程池的线程;如果大于 0,即使本地任务执行完毕,核心线程也不会销毁。这个值的设置非常关键,设置过大会浪费资源,设置过小会导致线程频繁地创建或销毁。
2 maximumPoolSize int 线程池能够容纳同时执行的最大线程数 >0并且>= corePoolSize 如果任务数超过第 5 个参数 workQueue 的任务缓存上限且待执行的线程数小于 maximumPoolSize 时,需要借助第 5 个参数的帮助,缓存在队列中。如果 maximumPoolSizecorePoolSize 相等,则是固定大小线程池。最大线程数 = 核心线程数 + 非核心线程数。
3 keepAliveTime long 线程池中的线程空闲时间 >=0 当空闲时间达到 keepAliveTime 值时,非核心线程会被销毁,直到只剩下 corePoolSize 个线程为止,避免浪费内存和句柄资源。在默认情况下,当线程池的线程数大于 corePoolSize 时,keepAliveTime 才会起作用。但是当 ThreadPoolExecutorallowCoreThreadTimeOut 变量设置为 true 时,核心线程超时后也会被回收。
4 unit TimeUnit 时间单位 keepAliveTime 的时间单位通常是 TimeUnit.SECONDS
5 workQueue BlockingQueue 任务缓存队列 不可以为 null 当请求的线程数大于等于 corePoolSize 时,任务会进入 BlockingQueue 阻塞队列等待执行。
6 threadFactory ThreadFactory 线程工厂 不可以为 null 用来生成一组相同任务的线程。线程池的命名是通过给这个 factory 增加组名前缀来实现的。在虚拟机栈分析时,就可以知道线程任务是由哪个线程工厂产生的。
7 handler RejectedExecutionHandler 执行拒绝策略的对象 不可以为 null 当待执行的线程数大于等于 maximumPoolSize 时,就可以通过该策略处理请求,这是一种简单的限流保护。

2.3.2 ThreadPoolExecutor 执行任务的规则是什么?

  1. 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务;
  2. 如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行;
  3. 如果在步骤 2 中无法将任务插入到任务队列中(原因往往是任务队列已满),这个时候如果线程数量未达到线程池规定的最大值,那么会启动一个非核心线程来执行任务;
  4. 如果步骤 3 中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务。

绘制流程图如下:

在这里插入图片描述

2.3.3 ThreadPoolExecutor 的 UML 类图是什么?

在这里插入图片描述

  • 顶层接口 Executor 提供了一种思想:将任务提交和任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需要提供 Runnable 对象,将任务的运行逻辑提交到执行器(Executor)中,由 Executor 框架完成线程的调度和任务的执行部分。
  • ExecutorService 接口继承了 Executor 接口,增加了一些能力:(1)扩充执行任务的能力,补充可以为一个或一批异步任务生成 Future 的方法;(2)提供了管控线程池的方法,如停止线程池的运行,判断线程池是否在运行。
  • AbstractExecutorService 是上层的抽象类,提供了对 ExecutorService 接口的默认实现(submit 方法、invokeAll 方法),添加了 newTaskFor 方法来返回一个 RunnableFuture,没有对核心方法 execute 进行实现。它的作用是将执行任务的流程串联了起来,保证下层的实现只需要关注一个执行任务的方法即可。
  • ThreadPoolExecutor 继承于 AbstractExecutorService 类,是一个具体实现类,它一方面维护自身的生命周期,另一方面同时管理任务和线程,使两者良好地结合从而执行并行任务。
  • ScheduledExecutorService 接口继承了 ExecutorService 接口,增加可安排在给定的延迟后运行或定期执行的能力。
  • ScheduledThreadPoolExecutor 继承于 ThreadPoolExecutor 类并实现了 ScheduledExecutorService 接口,在 ThreadPoolExecutor的基础上扩展了可安排在给定的延迟后运行或定期执行的能力。

2.3.4 Executors 里的线程池有哪些?这些线程池有什么特点?

比较项 newCachedThreadPool newFixedThreadPool(int nThreads) newSingleThreadExecutor newScheduledThreadPool(int corePoolSize)
corePoolSize 0 nThreads 1 corePoolSize
maximumPoolSize Integer.MAX_VALUE nThreads 1 Integer.MAX_VALUE
keepAliveTime 60L 0L 0L 0
unit TimeUnit.SECONDS TimeUnit.MILLISECONDS TimeUnit.MILLISECONDS TimeUnit.NANOSECONDS
workQueue new SynchronousQueue() new LinkedBlockingQueue() new LinkedBlockingQueue() new DelayedWorkQueue()
线程池名称 无界线程池,可以进行自动线程回收 固定大小线程池 单线程线程池 执行定时任务,重复任务线程池
特点 没有核心线程,只有非核心线程(最大为Integer.MAX_VALUE),超过 60s 的空闲线程会被回收,SynchronousQueue 会将任务直接提交给线程而不保持它们,所以任务会立即执行。 只有固定个数的核心线程,没有非核心线程,核心线程不会被回收,任务队列大小没有限制。当线程处于空闲状态时,不会被回收;当所有的线程都处于活动状态时,新任务到达就处于等待状态,直到有线程空闲出来。 只有一个核心线程,没有非核心线程,核心线程不会被回收,任务队列大小没有限制。可以保证所有的任务都在同一个线程中按顺序执行。 核心线程数是固定的,非核心线程数是没有限制的,非核心线程空闲时会被立即回收。

3. 最后

本文总结了 Android 中的线程和线程池。

限于篇幅对于线程池的总结没有深入,如线程池到底是如何复用线程的?线程池的源码分析?线程池里面如何体现生产者和消费者模式?对于这些内容,同学们可以查看参考部分的文章,这些文章都是值得多多研读的。

参考

  • Architecture(1)AsyncTask源码分析

  • 【源码】康一康过时的→AsyncTask

    说明了 AsyncTask 的版本演进

  • android Service的stopself方法

  • Java线程池实现原理及其在美团业务中的实践

    大厂出品,值得研读。

  • 每日一问 | 线程池中的线程是如何复用的?

  • Android 线程池全解析-Android轮子哥

    从源码上说明了复用线程到底是怎么回事。

  • 池化思想

    使用水龙头的例子来说明池化思想,赞!

  • JDK ThreadPoolExecutor核心原理与实践
    vivo 出品