当前位置: 首页 > news >正文

学习Android(十二)Thread

简介

Android 应用的 UI 操作都在主线程(Main Thread)执行,若在该线程中执行耗时任务,会导致界面卡顿甚至 ANR(应用无响应)。因此,需要在子线程中执行耗时、阻塞或复杂计算等任务,并通过合适的通信机制将结果回传给主线程。Android 提供了最原始的 Thread 类,和基于 Thread 封装的 HandlerThreadExecutorService(线程池)等工具,以简化异步编程和线程管理。下面分别介绍各部分的属性、方法及示例。

1. 线程概念与生命周期

Android 中每个应用都运行在独立进程内,进程至少包含一个主线程(UI 线程),可以通过创建更多 Thread 实例来并行执行任务。主线程负责界面绘制、事件分发等,子线程则执行耗时操作,避免阻塞主线程。良好的线程使用能显著提升应用性能,减少 UI 卡顿。良好的线程是使用能显著提升应用性能,减少UI卡顿。

线程的典型生命周期包括:

  • NEW:线程对象创建,但未调用 start()

  • RUNNABLE: 调用 start() 后,线程进入可运行状态,由系统调度;

  • BLOCKED、WAITING、TIMED_WAITING:线程被阻塞或等待;

  • TERMINATED:run() 方法执行完毕或抛出未捕获异常后结束。

2. Thread 类的核心属性与方法

class Thread : Runnable {// 构造函数constructor()constructor(runnable: Runnable)constructor(name: String)constructor(runnable: Runnable, name: String)// 启动线程,调用 run()fun start()// 线程执行入口(可由子类重写)override fun run()// 等待该线程终止fun join()// 中断线程fun interrupt()// 检查中断状态fun isInterrupted(): Boolean// 获取线程名称fun getName(): String// 设置线程名称fun setName(name: String)// 获取线程优先级(1~10,默认5)fun getPriority(): Int// 设置线程优先级fun setPriority(priority: Int)// 获取线程 IDfun getId(): Long// 获取线程状态fun getState(): Thread.State
}
  • start()run():调用 start() 后,JVM 创建新调用栈并最终执行 run();直接调用 run() 则在当前线程同步执行,不会新启线程。

  • join():让当前线程等待目标线程终止后再继续执行,可指定超时。

  • interrupt() / isInterrupted():用于中断阻塞中或正在运行的线程。

  • 线程名称、ID、优先级:名称便于调试,优先级(Thread.MIN_PRIORITY=1Thread.MAX_PRIORITY=10)影响调度,但 Android 上意义有限,默认优先级为 5。

3. HandlerThread 带消息循环的后台线程

HandlerThread 继承自 Thread ,内部自动创建一个 Looper 和消息队列,可以在该线程中创建 Handler 来处理消息或 Runable

  • 构造方法

    class HandlerThread(name: String, priority: Int = Process.THREAD_PRIORITY_DEFAULT)
    
  • 关键方法

    • start():启动线程并准备 Looper;

    • getLooper():返回线程内部的 Looper,在调用 start() 后可获取;

    • getThreadeHandler():可自行保存 Handler;

  • 实现原理:HandlerThread 重写了 run() ,在启动后先为当前线程调用 Looper.prepare(),然后 Looper.loop() 开始消息循环,最后退出循环时清理 Looper

  • 使用 HandlerThread() 示例

    // 创建并启动
    val handlerThread = HandlerThread("WorkerThread").apply { start() }
    // 在该线程中创建 Handler
    val workerHandler = Handler(handlerThread.looper)
    workerHandler.post {// 后台执行耗时操作val result = doHeavyWork()// 切回主线程更新 UImainHandler.post { updateUI(result) }
    }
    // 停止循环并释放
    handlerThread.quitSafely()
    

4. 线程池与 ExecutorService

直接创建 Thread 易导致线程数量时空且难管理,Java提供 Executor 框架用于统一管理线程池和任务调度。

interface Executor {void execute(Runnable command);
}interface ExecutorService extends Executor {Future<?> submit(Runnable task);<T> Future<T> submit(Callable<T> task);void shutdown();List<Runnable> shutdownNow();
}
  • Executors.newFixedThreadPool(n):固定大小线程池;

  • newCachedThreadPool():根据需要创建新线程,空闲线程 60 秒后回收;

  • newSingleThreadExecutor():单线程串行执行;

  • newScheduledThreadPool(n):支持延迟与周期执行。

线程池属性

  • CorePoolSize:核心线程数;

  • MaximumPoolSize:最大线程数;

  • KeepAliveTime:非核心线程空闲存活时间;

  • WorkQueue:任务队列;

  • ThreadFactory:自定义线程创建方式;

  • RejectedExecutionHandler:线程池满时的拒绝策略

5. 异步通信:Handler、Message、Looper

Android 提供 HandlerMessageMessageQueueLooper 等机制,用于线程间信息传递。

  • Looper.prepare() / Looper.loop():在子线程中初始化并启动消息循环;

  • Handler:绑定到某个 Looper,通过 sendMessage()post() 将任务投递到其所在线程的消息队列;

  • Message:携带 whatarg1arg2obj 等字段用于传参;

  • Handler.CallbackhandleMessage():处理消息;

子线程向主线程发送消息示例

// 在子线程中
val childHandler = Handler(Looper.getMainLooper()) // 绑定主线程
childHandler.post {// 更新 UItextView.text = "任务完成"
}// 在子线程自建 Looper
val thread = Thread {Looper.prepare()val handler = Handler { msg ->// 处理 msgtrue}Looper.loop()
}
thread.start()

6. 以下示例演示了如何用单独 Activity 界面来体验三种常见的线程使用方式

  • 原生 Thread

    ThreadActivity

    
    class ThreadActivity: AppCompatActivity() {companion object {const val TAG = "ThreadDemo"}private lateinit var statusText: TextViewprivate lateinit var startButton: Buttonprivate lateinit var interruptButton: Buttonprivate var demoThread: Thread? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 创建简单UIval layout = LinearLayout(this).apply {orientation = LinearLayout.VERTICALsetPadding(40, 40, 40, 40)}statusText = TextView(this).apply {text = "准备开始线程演示"textSize = 18f}startButton = Button(this).apply {text = "启动线程"}interruptButton = Button(this).apply {text = "中断线程"}layout.addView(statusText)layout.addView(startButton)layout.addView(interruptButton)setContentView(layout)val mainHandler = Handler(Looper.getMainLooper())// 示例:使用 Runnable 和命名构造函数创建线程val myRunnable = Runnable {Log.d(TAG,"线程开始,名称: ${Thread.currentThread().name}, ID: ${Thread.currentThread().id}")for (i in 1..5) {if (Thread.currentThread().isInterrupted) {Log.d(TAG, "线程被中断,退出")break}Log.d(TAG, "线程运行中:$i")mainHandler.post {statusText.text = "线程运行中:$i"}try {Thread.sleep(1000)} catch (e: InterruptedException) {Log.d(TAG, "线程睡眠中被中断")Thread.currentThread().interrupt() // 重置中断标志break}}mainHandler.post {statusText.text = "线程执行完毕"}Log.d(TAG, "线程结束")}startButton.setOnClickListener {if (demoThread?.isAlive == true) {Toast.makeText(this, "线程已在运行", Toast.LENGTH_SHORT).show()return@setOnClickListener}// 使用 Thread(Runnable, name) 构造函数demoThread = Thread(myRunnable, "MyCustomThread").apply {priority = Thread.NORM_PRIORITY // 设置优先级(1-10)}demoThread?.start()}interruptButton.setOnClickListener {demoThread?.interrupt()statusText.text = "已请求中断线程"}}
    }5000).also {isMonitoring.set(false)Log.d(TAG, "停止监控")}}
    }
    功能示例代码说明
    构造函数Thread(Runnable, name)使用具名构造函数创建线程
    start()demoThread?.start()启动线程
    sleep()Thread.sleep(1000)模拟耗时任务
    interrupt()demoThread?.interrupt()发送中断请求
    isInterrupted()Thread.currentThread().isInterrupted检测中断状态
    isAlive()demoThread?.isAlive判断线程是否仍在运行
    priorityThread.NORM_PRIORITY设置线程优先级(默认是 5)
  • HandlerThread

    HandlerThreadActivity

    class HandlerThreadActivity : AppCompatActivity() {companion object {const val TAG = "HandlerThreadDemo"}private lateinit var statusText: TextViewprivate lateinit var startButton: Buttonprivate lateinit var stopButton: Buttonprivate lateinit var handlerThread: HandlerThreadprivate lateinit var workHandler: Handleroverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 简单布局val layout = LinearLayout(this).apply {orientation = LinearLayout.VERTICALsetPadding(40, 40, 40, 40)}statusText = TextView(this).apply {text = "HandlerThread 示例初始化"textSize = 18f}startButton = Button(this).apply { text = "开始任务" }stopButton = Button(this).apply { text = "停止线程" }layout.addView(statusText)layout.addView(startButton)layout.addView(stopButton)setContentView(layout)// 初始化 HandlerThreadhandlerThread = HandlerThread("MyHandlerThread", Process.THREAD_PRIORITY_BACKGROUND).apply {start() // 启动线程(必须)}// 创建工作 Handler(绑定到 handlerThread 的 Looper)workHandler = object : Handler(handlerThread.looper) {override fun handleMessage(msg: Message) {Log.d(TAG, "handleMessage(): what=${msg.what}, thread=${Thread.currentThread().name}")Thread.sleep(1000) // 模拟耗时runOnUiThread {statusText.text = "完成消息:${msg.what}"}}}startButton.setOnClickListener {if (!handlerThread.isAlive) {Toast.makeText(this, "线程已关闭,请重启 Activity", Toast.LENGTH_SHORT).show()return@setOnClickListener}for (i in 1..5) {workHandler.post {Log.d(TAG, "执行第 $i 个任务 on ${Thread.currentThread().name}")Thread.sleep(500)runOnUiThread {statusText.text = "任务 $i 完成"}}// 可发送 messageworkHandler.sendMessage(Message.obtain().apply { what = i + 100 })}}stopButton.setOnClickListener {Log.d(TAG, "准备退出 HandlerThread")handlerThread.quitSafely()statusText.text = "线程已请求退出"}}override fun onDestroy() {super.onDestroy()if (handlerThread.isAlive) {handlerThread.quitSafely()}}
    }
    特性表现
    消息循环(Looper)handlerThread.looper 提供一个后台消息循环
    线程绑定 Handler使用 Handler(handlerThread.looper) 把任务放到该线程
    任务处理支持 post(Runnable)sendMessage(Message)
    退出方式使用 quitSafely() 可在处理完当前消息后安全退出
    • 运行结果:

      • 点击「开始任务」后,会有 5 个任务与 5 个 message 被发送到 HandlerThread 线程处理

      • TextView 会显示每个任务的完成信息

      • 点击「停止线程」后会安全退出 HandlerThread

  • ThreadHandlerThread 对比

    项目ThreadHandlerThread
    类型Java 基础线程类Android 对 Thread 的增强
    是否带消息队列❌ 没有✅ 内部有 Looper 和消息队列
    是否能重复接收任务❌ 通常一次性使用✅ 可以通过 Handler 多次发送任务
    通常用途简单的一次性耗时任务需要持续处理任务或消息的后台线程
    特性ThreadHandlerThread
    启动方式thread.start()handlerThread.start()(再取 looper)
    任务提交方式重写 run() 或传入 Runnable使用绑定的 Handler.post()Handler.sendMessage()
    是否有消息队列❌ 无✅ 有(支持多任务队列)
    是否有 Looper❌ 无✅ 有(通过 handlerThread.looper 获取)
    生命周期一次性执行任务后结束持续运行,直到调用 quit()quitSafely()
    可扩展性灵活但手动管理多任务麻烦内置消息机制,适合长时间后台工作
    使用场景简单短任务,快速执行后台线程处理消息/任务(如日志、IO、图片压缩)

    Thread 做事快,用完就走;HandlerThread 持久干,排队处理不烦。

  • 线程池 ExecutorService

    ThreadPoolActivity

    class ThreadPoolActivity : AppCompatActivity() {companion object {const val TAG = "ThreadPoolDemo"}private lateinit var executorService: ExecutorServiceprivate val futureTasks = mutableListOf<Future<*>>()private lateinit var startButton: Buttonprivate lateinit var cancelButton: Buttonprivate lateinit var statusText: TextViewprivate var taskCounter = 0override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// ========== 简单 UI ==========val layout = LinearLayout(this).apply {orientation = LinearLayout.VERTICALsetPadding(40, 40, 40, 40)}startButton = Button(this).apply { text = "提交任务" }cancelButton = Button(this).apply { text = "取消所有任务" }statusText = TextView(this).apply {text = "线程池未启动任务"textSize = 16f}layout.addView(startButton)layout.addView(cancelButton)layout.addView(statusText)setContentView(layout)// ========== 初始化线程池 ==========executorService = Executors.newFixedThreadPool(3)// ========== 启动任务 ==========startButton.setOnClickListener {submitMultipleTasks(5)}// ========== 取消任务 ==========cancelButton.setOnClickListener {for ((index, future) in futureTasks.withIndex()) {if (!future.isDone) {future.cancel(true)Log.d(TAG, "尝试取消任务 ${index + 1}")}}statusText.text = "已尝试取消所有任务"}}private fun submitMultipleTasks(count: Int) {for (i in 1..count) {val taskId = ++taskCounterval future = executorService.submit(Callable<String> {val threadName = Thread.currentThread().nameLog.d(TAG, "任务 $taskId 开始,线程:$threadName")try {repeat(5) { step ->if (Thread.currentThread().isInterrupted) {Log.d(TAG, "任务 $taskId 被中断")return@Callable "任务 $taskId 中断"}Log.d(TAG, "任务 $taskId 执行步骤 ${step + 1},线程:$threadName")Thread.sleep(500)}} catch (e: InterruptedException) {Log.d(TAG, "任务 $taskId 捕获中断异常")return@Callable "任务 $taskId 被异常中断"}Log.d(TAG, "任务 $taskId 完成,线程:$threadName")return@Callable "任务 $taskId 完成"})futureTasks.add(future)// 单独线程监控结果(避免主线程阻塞)Thread {try {val result = future.get()Log.d(TAG, "任务 $taskId 结果: $result")runOnUiThread {statusText.text = "任务 $taskId 完成"}} catch (e: Exception) {Log.d(TAG, "任务 $taskId 异常:${e.message}")}}.start()}}override fun onDestroy() {super.onDestroy()Log.d(TAG, "Activity 销毁,强制关闭线程池")executorService.shutdownNow()}}
    ExecutorService 特点展示方式
    线程复用FixedThreadPool(3),通过 Thread.currentThread().name 可见
    并发处理多个任务同时提交 5 个任务,线程池自动调度
    取消任务使用 Future.cancel(true) 中断
    获取结果每个任务用 future.get() 单独监控
    主线程与子线程通信runOnUiThread 更新状态到 TextView

7. 线程相关面试题

  • 如何创建一个线程?有哪几种方式?

    • 继承 Thread:创建一个类继承 Thread,并重写其 run() 方法。

    • 实现 Runnable 接口:创建一个实现了 Runnable 接口的类,并将其实例作为参数传递给 Thread 类的构造函数。

    这两种方式都可以创建并启动线程,但实现 Runnable 接口的方式更灵活,适用于多线程共享资源的场景。

  • start()run() 方法的区别?

    • start() 方法:用于启动新线程,调用后 JVM 会创建新的线程并执行 run() 方法。

    • run() 方法:包含线程要执行的任务代码。如果直接调用 run() 方法,则不会创建新线程,而是在当前线程中执行。

  • 线程的生命周期有哪些状态?

    线程的生命周期包括以下几种状态:

    • 新建(New):线程对象已创建,但尚未启动。

    • 就绪(Runnable):线程已启动,等待被线程调度器调度执行。

    • 运行(Running):线程正在执行。

    • 阻塞(Blocked):线程被阻塞,等待获取锁。

    • 等待(Waiting):线程进入等待状态,等待其他线程的通知。

    • 计时等待(Timed Waiting):线程在指定时间内等待。

    • 终止(Terminated):线程执行完毕或被中断。

  • 如何实现线程间的通信?

    可以使用 wait()notify()notifyAll() 方法实现线程间通信。这些方法必须在同步块或同步方法中调用,以确保线程安全。

  • 如何实现线程的中断?

    可以调用线程对象的 interrupt() 方法来中断线程。在线程的执行代码中,应定期检查 isInterrupted() 状态,或捕获 InterruptedException 异常,以响应中断请求。

  • 什么是 HandlerThread

    HandlerThread 是 Android 中的一个类,它继承自 Thread,并自带一个 Looper。这使得我们可以在子线程中创建 Handler,从而处理消息队列,实现消息的异步处理。

  • HandlerThread 的使用场景有哪些?

    HandlerThread 适用于需要在后台线程中处理消息或执行耗时操作的场景,如:

    • 处理后台任务队列。

    • 执行耗时的初始化操作。

    • 避免在主线程中执行耗时操作导致的 ANR(应用无响应)。

  • 如何停止 HandlerThread

    可以调用 quit()quitSafely() 方法来停止 HandlerThread

    • quit() 方法:立即停止 Looper,可能会导致消息队列中的消息未被处理完。

    • quitSafely() 方法:等待消息队列中的消息处理完毕后,再停止 Looper,更安全。

  • 什么是 ExecutorService

    ExecutorService 是 Java 中的一个接口,提供了管理和控制线程池的功能。通过它可以实现任务的异步执行、线程的复用和资源的有效管理。

  • ExecutorService 的主要实现类有哪些?

    ExecutorService 的常用实现类包括:

    • ThreadPoolExecutor:最常用的线程池实现类,提供了灵活的线程池配置。

    • ScheduledThreadPoolExecutor:支持任务的定时和周期性执行。

  • 如何创建线程池?

    可以使用 Executors 工具类提供的静态方法创建线程池,如:

    • newFixedThreadPool(int nThreads):创建固定大小的线程池。

    • newCachedThreadPool():创建可根据需要创建新线程的线程池。

    • newSingleThreadExecutor():创建单线程的线程池。

    • newScheduledThreadPool(int corePoolSize):创建支持定时和周期性任务执行的线程池。

  • 如何提交任务并获取执行结果?

    可以使用 submit() 方法提交实现了 Callable 接口的任务,该方法会返回一个 Future 对象,通过 Future.get() 方法可以获取任务的执行结果。

  • 如何优雅地关闭线程池?

    可以使用以下方法关闭线程池:

    • shutdown() 方法:启动线程池的有序关闭过程,已提交的任务会继续执行,但不再接受新任务。

    • shutdownNow() 方法:尝试停止所有正在执行的任务,并返回等待执行的任务列表。

  • 如何确保线程不会造成内存泄漏?

    • 避免线程持有长生命周期对象的强引用

    • 及时停止不再需要的线程

    • 清除未处理的消息和回调

    • 正确使用线程池

    • 使用弱引用管理线程中的资源

    • 定期进行内存泄漏检测

  • 如何保证线程执行的先后顺序

    • 使用 Thread.join() 方法: join() 方法会使当前线程等待指定线程执行完毕后再继续执行,从而实现线程的顺序执行。

    • 使用 CountDownLatch : CountDownLatch 是一个同步辅助类,它允许一个或多个线程等待直到在其他线程中执行的一组操作完成。

    • 使用单线程线程池 Executors.newSingleThreadExecutor(): 单线程线程池会按照任务提交的顺序依次执行各个任务,从而保证了线程的执行顺序。

    • 使用 synchronizedwait()notify(): 通过对象锁和线程间的通信方法,可以精确控制线程的执行顺序。

    • 使用 ReentrantLockCondition: ReentrantLock 提供了比 synchronized 更灵活的锁机制,配合 Condition 可以实现更精细的线程控制。

相关文章:

  • 深度解析 Element Plus
  • java上机测试错题回顾(1)
  • Ubuntu学习记录
  • EXIST与JOIN连表比较
  • Flink基本理解
  • 缓存穿透、缓存击穿、缓存雪崩解决方案
  • MySQL 索引详解与原理分析
  • Typescript总结篇——配置TS、基础知识(类型、接口、类型别名、泛型、extendsinfer关键字)
  • 递归+反射+注解(动态拼接建表语句)
  • BitsAndBytesConfig参数描述
  • RESTful风格
  • C++网络编程入门学习(四)-- GDB 调试 学习 笔记
  • 面试题 - 微服务相关的经典问题(33道)
  • 解决echarts图表legend文本太长;echarts图表的图例legend省略号显示
  • 第十节第四部分:常见API:秒杀案例、Calendar
  • SkyWalking 报错:sw_profile_task 索引缺失问题分析与解决
  • Javascript 编程基础(4)函数 | 4.4、bind() 方法
  • 重磅升级!Google Play商店改版上线
  • 13、自动配置【源码分析】-自动包规则原理
  • Postgres数据库配置用户读写权限(read_write)和只读权限(read_only):
  • 营销最好的方法/北京seo排名方法