Android 多线程实现方式

article/2025/9/15 7:15:41

一个优秀的程序员,必须具备两种能力:学习能力,时间管理能力

该原创文章首发于微信公众号“字节流动”

Android 多线程实现方式

通常来说,一个应用至少有一个进程,而一个进程至少有一个线程。

线程是 CPU 调度的基本单位,进程是系统资源分配的基本单位。

进程独享内存资源,一个进程可以看作一个 JVM ,一个进程崩溃后,在保护模式下一般不会对其它进程产生影响。
同一个进程中的线程共享内存资源,一个线程死掉就导致整个进程死掉。

Android 提供了四种常用的多线程实现方式:

  • AsyncTask
  • 异步消息机制
  • IntentService
  • ThreadPoolExcutor

AsyncTask

我们的老朋友 AsyncTask 类,它是封装好的线程池,操作 UI 线程极其方便。

瞅一眼,AsyncTask 的三个泛型参数:

public abstract class AsyncTask<Params, Progress, Result>

  • params传入参数类型,即 doInBackground() 方法中的参数类型;
  • Progress ,异步任务执行过程中返回的任务执行进度类型,即 publishProgress() 和onProgressUpdate() 方法中传入的参数类型;
  • Result ,异步任务执行完返回的结果类型,即 doInBackground() 方法中返回值的类型。

四个回调方法:

  • onPreExecute(),在主线程执行,做一些准备工作。
  • doInBackground(),在线程池中执行,该方法是抽象方法,在此方法中可以调用 publishProgress() 更新任务进度。
  • onProgressUpdate(),在主线程中执行,在 publishProgress() 调用之后被回调,展示任务进度。
  • onPostExecute(),在主线程中执行,异步任务结束后,回调此方法,处理返回结果。

注意

  • 当 AsyncTask 任务被取消时,回调 onCanceled(obj) ,此时 onPostExecute(),不会被调用,AsyncTask 中的 cancel() 方法并不是真正去取消任务,只是设置这个任务为取消状态,需要在 doInBackground() 中通过 isCancelled() 判断终止任务。
  • AsyncTask 必须在主线程中创建实例,execute() 方法也必须在主线程中调用。
  • 每个 AsyncTask 实例只能执行一次 execute() ,多次执行会报错,如需执行多次,则需创建多个实例。
  • Android 3.0 之后, AsyncTask 对象默认执行多任务是串行执行,即 mAsyncTask.execute() ,并发执行的话需要使用 executeOnExecutor() 。
  • AsyncTask 用的是线程池机制和异步消息机制(基于 ThreadPoolExecutor 和 Handler )。Android 2.3 以前,AsyncTask 线程池容量是 128 ,全局线程池只有 5 个工作线程,如果运用 AsyncTask 对象来执行多个并发异步任务,那么同一时间最多只能有 5 个线程同时运行,其他线程将被阻塞。Android 3.0 之后 Google 又进行了调整,新增接口 executeOnExecutor() ,允许自定义线程池(那么核心线程数以及线程容量也可自定义),并提供了 SERIAL_EXECUTOR 和 THREAD_POOL_EXECUTOR 预定义线程池。后来 Google 又做了一些调整(任何事物都不完美),将线程池的容量与 CPU 的核心数联系起来,如目前 SDK 25 版本中,预定义的核心线程数量最少有 2 个,最多 4 个,线程池容量范围 5 ~ 9 。改动如下:
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();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;

异步消息机制

异步消息机制的三大主角: Handler ,Message 和 Looper 。
Looper 负责创建 MessageQueue 消息对列,然后进入一个无限 for 循环中,不断地从消息队列中取消息,如果消息队列为空,当前线程阻塞,Handler 负责向消息队列中发送消息。

Looper

Looper 有两个重要的方法: prepare() 和 loop()。

  • prepare() , Looper 与当前线程绑定,一个线程只能有一个 Looper 实例和一个 MessageQueue 实例。
public static final void prepare() {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(true)); 保证 Looper 对象在当前线程唯一
}// Looper 的构造方法
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mRun = true;mThread = Thread.currentThread();
}
  • loop ,进入一个无限 for 循环体中,不断地从消息队列中取消息,然后交给消息的 target 属性的 dispatchMessage 方法去处理。
public static void loop() {final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue;// Make sure the identity of this thread is that of the local process,// and keep track of what that identity token actually is.Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();// 无限循环体,有没有想过在 UI 线程里,有这样一个死循环,为什么界面没卡死??// 答案最后揭晓。for (;;) { Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}msg.target.dispatchMessage(msg);msg.recycle();}
}

Handler

Handler 负责向消息队列中发送消息。
在 Activity 中我们直接可以 new Handler ,那是因为在 Activity 的启动代码中,已经在当前 UI 线程中调用了 Looper.prepare() 和 Looper.loop() 方法。

在子线程中 new Handler 必须要在当前线程(子线程)中创建好 Looper 对象和消息队列,代码如下

    //在子线程中Looper.prepare();handler = new Handler() {public void handleMessage(Message msg) {//处理消息};};Looper.loop();   

之后,你拿着这个 Handler 对象就可以在其他线程中,往这个子线程的消息队列中发消息了。

HandlerThread

HandlerThread 可以看作在子线程中创建一个异步消息处理机制的简化版,HandlerThread 对象自动帮我们在工作线程里创建 Looper 对象和消息队列。

使用方法:

mHandlerThread = new HandlerThread("MyHandlerThread");
mHandlerThread.start();mHandler = new Handler(mHandlerThread.getLooper()){@Overridepublic void handleMessage(Message msg) {//处理消息}
};

之后你就可以使用 Handler 对象往工作线程中的消息队列中发消息了。

看一下源码片段:

public class HandlerThread extends Thread {int mPriority;int mTid = -1;Looper mLooper;public HandlerThread(String name) {super(name);mPriority = Process.THREAD_PRIORITY_DEFAULT;}protected void onLooperPrepared() {}@Overridepublic void run() {mTid = Process.myTid();Looper.prepare();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 {wait();} catch (InterruptedException e) {}}}return mLooper;}
}

注意:handler 在 UI 线程中初始化的,looper 在一个子线程中执行,我们必须等 mLooper 创建完成之后,才能调用 getLooper ,源码中是通过 wait 和 notify 解决两个线程的同步问题。

IntentService

IntentService 可以看成是 Service 和 HandlerThread 的合体。它继承自 Service ,并可以处理异步请求,其内部有一个 WorkerThread 来处理异步任务,当任务执行完毕后,IntentService 自动停止。

如果多次启动 IntentService 呢? 看到 HandlerThread ,你就应该想到多次启动 IntentService ,就是将多个异步任务放到任务队列里面,然后在 onHandlerIntent 回调方法中串行执行,执行完毕后自动结束。

下面对源码进行简单的解析,IntentService 源码:

public abstract class IntentService extends Service {private volatile Looper mServiceLooper;private volatile ServiceHandler mServiceHandler;private String mName;private boolean mRedelivery;private final class ServiceHandler extends Handler {public ServiceHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {//onHandleIntent 方法在工作线程中执行,执行完调用 stopSelf() 结束服务。onHandleIntent((Intent)msg.obj);stopSelf(msg.arg1);}}/*** Creates an IntentService.  Invoked by your subclass's constructor.** @param name Used to name the worker thread, important only for debugging.*/public IntentService(String name) {super();mName = name;}/*** enabled == true 时,如果任务没有执行完,当前进程就死掉了,那么系统就会令当前进程重启。* 任务会被重新执行。*/public void setIntentRedelivery(boolean enabled) {mRedelivery = enabled;}@Overridepublic void onCreate() {super.onCreate();// 上面已经讲过,HandlerThread 对象 start 之后,会在工作线程里创建消息队列 和 Looper 对象。HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");thread.start();mServiceLooper = thread.getLooper();// 获得 Looper 对象初始化 Handler 对象。mServiceHandler = new ServiceHandler(mServiceLooper);}@Overridepublic void onStart(@Nullable Intent intent, int startId) {// IntentService 每次启动都会往工作线程消息队列中添加消息,不会创建新的线程。Message msg = mServiceHandler.obtainMessage();msg.arg1 = startId;msg.obj = intent;mServiceHandler.sendMessage(msg);}// 官方建议 IntentService onStartCommand 方法不应该被重写,注意该方法会调用 onStart 。@Overridepublic int onStartCommand(@Nullable Intent intent, int flags, int startId) {onStart(intent, startId);return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;}@Overridepublic void onDestroy() {  //服务停止会清除消息队列中的消息,除了当前执行的任务外,后续的任务不会被执行。mServiceLooper.quit();}/*** 不建议通过 bind 启动 IntentService ,如果通过 bind 启动 IntentService ,那么 onHandlerIntent 方法不会被回调。Activity 与 IntentService 之间的通信一般采用广播的方式。*/@Override@Nullablepublic IBinder onBind(Intent intent) {return null;}/*** 子类必须要实现,执行具体的异步任务逻辑,由 IntentService 自动回调。*/@WorkerThreadprotected abstract void onHandleIntent(@Nullable Intent intent);
}

IntentService 源码很容易理解,你也可以就自己的应用场景封装自己的 IntentService 。

场景

  • 正常情况下,启动 IntentService ,任务完成,服务停止;
  • 异步任务完成前,停止 IntentService ,服务停止,但任务还会执行完成,完成后,工作线程结束;
  • 多次启动 IntentService ,任务会被一次串行执行,执行结束后,服务停止;
  • 多次启动 IntentService ,在所有任务执行结束之前,停止 IntentService ,服务停止,除了当前执行的任务外,后续的任务不会被执行;

ThreadPoolExcutor

图片来自 Jakob Jenkov 博客

ThreadPool

用来管理一组工作线程,任务队列( BlockingQueue )中持有的任务等待着被线程池中的空闲线程执行。

常用构造方法:

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue
);
  1. corePoolSize 核心线程池容量,即线程池中所维持线程的最低数量。corePoolSize 初始值为 0 ,当有新任务加入到任务队列中,新的线程将被创建,这个时候即使线程池中存在空闲线程,只要当前线程数小于 corePoolSize ,那么新的线程依然被创建。
  2. maximumPoolSize 线程池中所维持线程的最大数量。
  3. keepAliveTime 空闲线程在没有新任务到来时的存活时间。
  4. unit 参数 keepAliveTime 的时间单位。
  5. workQueue 任务队列,必须是 BlockingQueue 。

简单使用

创建 ThreadFactory ,当然也可以自定义。
    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());}};
创建 ThreadPoolExecutor 。
// 根据 CPU 核心数确定线程池容量。
public static final int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors(); mThreadPoolExecutor = new ThreadPoolExecutor(NUMBER_OF_CORES * 2, NUMBER_OF_CORES * 2 + 1,60L,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(),backgroundPriorityThreadFactory
);
执行
mThreadPoolExecutor.execute(new Runnable() { @Override  public void run() {  //do something  } });Future future = mThreadPoolExecutor.submit(new Runnable() { @Override  public void run() {  //do something  } });//任务可取消
future.cancel(true);Future<Integer> futureInt = mThreadPoolExecutor.submit(new Callable<Integer>() {@overridepublic Integer call() throws Exception {return 0;}});//获取执行结果
futureInt.get();FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>(){@overridepublic Integer call() throws Exception {return 0;}    });mThreadPoolExecutor.submit(task);
task.get();

联系与交流

微信公众号
我的公众号
个人微信
我的微信


http://chatgpt.dhexx.cn/article/BchN2GxK.shtml

相关文章

Java多线程,Android多线程

目录 一、线程的概念 二、线程创建的方式及特点 三、线程创建方式 1、继承Thread类 2、实现Runnable接口 3、实现Callable接口&#xff08;我觉得了解即可&#xff09; 4、AsyncTask异步任务&#xff08;被弃用&#xff09; 5、AsyncTask替代方案 四、线程的基础操作 …

Android多线程开发详解

一、基本概念 1、时间片轮转机制 如果在时间片结束时进程还在运行&#xff0c;则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结来,则CPU当即进行切换。调度程序所要做的就是维护一张就绪进程列表,当进程用完它的时间片后,它被移到队列的末尾。 每个进程被分…

Android开发中四种常用的多线程实现方式

前言 一般来说&#xff0c;一个应用至少有一个进程&#xff0c;一个进程至少有一个线程。线程是CPU调度的基本单位&#xff0c;进程是系统资源分配的基本单位。 进程拥有独占的内存资源&#xff0c;一个进程可以看作一个JVM一个进程崩溃后&#xff0c;一般不会影响保护模式下…

Android 中的多线程简介

一、概念讲解 进程&#xff1a;是程序运行过程中系统进行资源分配和调度的一个独立单位&#xff0c;使多个程序可 并发执行&#xff0c;以提高系统的资源利用率和吞吐量。 线程&#xff1a;一个基本的CPU执行单元 & 程序执行流的最小单元。 线程自己不拥有系统资源&#…

anchor free和anchor based的区别

链接&#xff1a;https://www.zhihu.com/question/356551927/answer/926659692 1.目标检测算法一般可分为anchor-based、anchor-free、两者融合类&#xff0c;区别就在于有没有利用anchor提取候选目标框。A. anchor-based类算法代表是fasterRCNN、SSD、YoloV2/V3等fasterRCNN-…

Anchor based and Anchor free(无锚VS有锚)【总结】

anchor-free 和 anchor-based 区别 anchor-free和anchor-based是两种不同的目标检测方法&#xff0c;区别在于是否使用预定义的anchor框来匹配真实的目标框。 anchor-based方法使用不同大小和形状的anchor框来回归和分类目标&#xff0c;例如faster rcnn、retinanet和yolo等。a…

2 anchor-base和anchor_free两者的优缺点

anchor-base和anchor_free两者的优缺点 anchor-base和anchor_free两者的优缺点 一、什么是anchor二、anchor-base和anchor-free的区别三、anchor-free和single anchor三、anchor-base和anchor-free的优缺点 参考 一、什么是anchor 从字面的意思解释&#xff0c;anchor就是船锚…

Anchor-Free总结

目录 Anchor-Free综述 一. CornerNet 1.1 概述1.2 模块介绍 1.2.1 Heatmap1.2.2 Offset1.2.3 Grouping Corners1.2.4 Corner Pooling1.3 总结二. CenterNet 2.1 概述2.2 Center-Regression三. FCOS 3.1. 概述3.2. 模块介绍 3.2.1 论文思路简介3.3.2 回归形式3.3 参考文献四 ATS…

【AI面试】Anchor based 、 Anchor free 和 no anchor 的辨析

深度学习的目标检测算法,通常会在输入图像中采样大量的区域,然后判断这些区域中是否包含我们感兴趣的目标,并调整(回归)区域边界,从而更准确地预测目标的真实边界框(ground-truth bounding box)。 目标检测算法会需要做两个事情: 推荐区域框是否有目标(positive or …

一文读懂anchor-base和anchor-free

1. 从Faster-RCNN看Anchor Faster-RCNN相对于Fast-RCNN的一个改进是引入了RPN网络&#xff0c;RPN用于区域推荐&#xff0c;替换了此前的SS算法使得网络在整体上更加的CNN化。那么RPN是怎么进行区域推荐的&#xff1f; 简单来说RPN先列举出数万个矩形框&#xff0c;然后用卷积…

目标检测3--AnchorFree的FCOS

文章目录 1.介绍2.FCOS中使用的方法2.1 网络结构2.2FCOS中使用FPN的多级预测2.3FCOS中的中心度 3.mmdetection中FCOS源码参考资料 欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f339; 1.介绍 论文:《FCOS: Fully Convolutional One-Stage Obj…

浅谈Anchor-Free发展历程

1.早期探索&#xff1a; DenseBox: https://arxiv.org/abs/1509.04874 YOLO: https://arxiv.org/abs/1506.02640 2.基于关键点&#xff1a; CornerNet: https://arxiv.org/abs/1808.01244 ExtremeNet: https://arxiv.org/abs/1901.08043 3.密集预测: FSAF: https://arxiv.org/a…

Anchor-Free系列之FCOS:A Simple and Strong Anchor-free Object Detector

Anchor-Free系列之CornerNet: Detecting Objects as Paired Keypoints_程大海的博客-CSDN博客 Anchor-Free系列之CenterNet&#xff1a;Objects as Points_程大海的博客-CSDN博客 Anchor-Free系列之FCOS&#xff1a;A Simple and Strong Anchor-free Object Detector_程大海的…

Anchor Based和Anchor Free

Anchor Based和Anchor Free之间区别主要有以下两点&#xff1a;1.分类差异&#xff08;关键正负样本定义&#xff09;2.回归差异 1.分类差异&#xff1a; 现阶段的算法多尺度预测&#xff0c;即GT是由哪一个特征层和位置Anchor预测。 Anchor Based是由IoU来确定哪层和哪个位置…

解读《Bridging the Gap Between Anchor-based and Anchor-free Detection》

张士峰大佬近期发了一篇论文解读Anchor-base和Anchor-free方法间的差别&#xff0c;其本质在于正负样本的选取方式不同。 论文&#xff1a;《Bridging the Gap Between Anchor-based and Anchor-free Detection via Adaptive Training Sample Selection》 链接&#xff1a;ht…

anchor-free方法总结

cornernet&#xff0c;centernet&#xff0c;onenet&#xff0c;fcos 这几篇论文的引用关系&#xff08;提出先后顺序&#xff09;&#xff1a; 将按照上面的顺序&#xff0c;从背景、标签分配等方面说明区别于联系。 一、背景&#xff1a; Cornernet&#xff1a;认为使用a…

anchor free和anchor base

仅供个人学习使用 1、anchor base anchor base的方法需要先在图片上生成候选框&#xff0c;无论是RPN生成还是通过k-means生成的先验框&#xff0c;都需要在分类回归之前有存在的框可使用。在框的基础上进行之后的操作。 超参数较为难调&#xff0c;正负样本不平衡&#xff…

Anchor free的心得

问题&#xff1a; 没有了Anchor框的监督信息&#xff0c;我们怎么针对检测任务做到正确回归&#xff1f; 本质&#xff1a;样本与ground truth的对应&#xff0c;如何选择合适样本与真实场景对应 Anchor&#xff1a; 其加入降低了回归问题难度&#xff0c;为分类问题提供选择…

Anchor-based 与 Anchor-free

参考 Anchor-based 与 Anchor-free - 云社区 - 腾讯云 1. Feature Selective Anchor-Free Module for Single-Shot Object Detection 参考&#xff1a;CVPR2019 | CMU提出Single-Shot目标检测最强算法&#xff1a;FSAF 2. FCOS: Fully Convolutional One-Stage Object Det…

Anchor-free

找到了一个说在工业领域很好的 目标检测 下面几篇paper有异曲同工之妙&#xff0c;开启了anchor-based和anchor-free的轮回。 1. Feature Selective Anchor-Free Module for Single-Shot Object Detection 2. FCOS: Fully Convolutional One-Stage Object Detection 3. Fo…