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

本地线程(Native Thread)、主线程(UI 线程) 和 子线程(Java 子线程)

1. 主线程(Main Thread / UI Thread)

特点

  • 唯一性:每个 Android 进程只有一个主线程。

  • 职责

    • 处理 UI 渲染(如 View 绘制、触摸事件)

    • 执行 Activity/Fragment 生命周期回调

  • 限制

    • 不能执行耗时操作(如网络请求、文件读写),否则会导致 ANR(Application Not Responding)。

    • 所有 UI 操作(如 TextView.setText())必须运行在主线程。

代码示例

// 在主线程更新 UI
textView.text = "Hello, UI Thread!"

2. 子线程(Java 子线程)

特点

  • 由 JVM 管理:通过 Java 的 ThreadExecutorService 或 Kotlin 协程创建。

  • 典型用途

    • 网络请求(Retrofit、OkHttp)

    • 数据库操作(Room)

    • 文件 I/O

  • 与主线程通信

    • 通过 HandlerLiveData 或 runOnUiThread 切换回主线程更新 UI。

代码示例

// 方式1:使用 Thread
Thread {// 在子线程执行耗时任务val result = fetchDataFromNetwork()runOnUiThread { // 切回主线程更新 UItextView.text = result}
}.start()// 方式2:使用协程(推荐)
lifecycleScope.launch(Dispatchers.IO) {val result = fetchDataFromNetwork()withContext(Dispatchers.Main) { // 切回主线程textView.text = result}
}

3. 本地线程(Native Thread)

特点

  • 由 C/C++ 创建:通过 std::thread 或 pthread 实现,运行在 Native 层(不受 JVM 直接管理)。

  • 典型用途

    • 高性能计算(如音视频编解码、图像处理)

    • 调用底层硬件(如 OpenGL、传感器)

  • 与 Java 交互

    • 必须手动附加到 JVM(AttachCurrentThread)才能调用 Java 方法。

    • 线程结束时需分离(DetachCurrentThread),否则可能内存泄漏。

代码示例

// Native 层代码(C++)
#include <jni.h>
#include <thread>JavaVM* g_jvm; // 全局保存 JVM 引用void nativeThreadTask(JNIEnv* env, jobject callback) {// 1. 执行耗时计算int result = heavyComputation();// 2. 回调 Java 方法jclass clazz = env->GetObjectClass(callback);jmethodID method = env->GetMethodID(clazz, "onResult", "(I)V");env->CallVoidMethod(callback, method, result);
}void startNativeThread(JNIEnv* env, jobject callback) {std::thread([callback] {// 1. 附加当前线程到 JVMJNIEnv* env;g_jvm->AttachCurrentThread(&env, nullptr);// 2. 执行任务nativeThreadTask(env, callback);// 3. 分离线程g_jvm->DetachCurrentThread();}).detach(); // 让线程独立运行
}

三者的核心区别

特性主线程子线程(Java)本地线程(Native)
创建方式Android 系统自动创建Thread/Coroutinestd::thread/pthread
运行环境JVM 管理的 UI 线程JVM 管理的线程池操作系统直接调度的线程
能否操作 UI✅ 能❌ 不能(需切回主线程)❌ 不能(需 JNI 回调主线程)
耗时操作支持❌ 禁止(会 ANR)✅ 适合✅ 适合(性能更高)
与 Java 交互直接调用直接调用需手动附加/分离 JVM

如何选择线程模型?

  1. 需要更新 UI? → 用主线程。

  2. 需要执行 I/O 或网络请求? → 用 Java 子线程或协程。

  3. 需要高性能计算(如 FFmpeg、OpenCV)? → 用本地线程 + JNI。

  4. 需要跨线程通信? → 主线程和子线程用 Handler,本地线程用 JNI Callback


常见问题

Q1:本地线程不附加 JVM 会怎样?

  • 调用 JNI 方法会崩溃(JNIEnv* 无效)。

  • 必须通过 AttachCurrentThread 获取有效的 JNIEnv

Q2:主线程为什么不能阻塞?

  • Android 要求主线程在 5 秒内 响应输入事件,否则触发 ANR。

Q3:协程是子线程吗?

  • 协程是轻量级线程管理工具,底层仍运行在 JVM 线程池上,属于 逻辑上的子线程


总结

  • 主线程:唯一,管 UI,不能阻塞。

  • 子线程:Java 层多任务,适合 I/O。

  • 本地线程:C++ 层高性能计算,需手动管理 JNI 环境。

1. 本地线程可以创建多个吗?

✅ 可以无限创建(理论)
本地线程是通过 C/C++ 的 std::thread 或 pthread 直接调用操作系统 API 创建的,不受 JVM 管理,因此:

  • 没有硬性数量限制(不像 Java 线程受线程池约束)。

  • 实际能创建的线程数取决于 设备资源(CPU 核心数、内存等)。

⚠️ 但需谨慎
创建过多本地线程会导致:

  • 内存耗尽(每个线程默认占用 1~2MB 栈内存)

  • CPU 频繁切换线程,性能下降

  • 增加 JNI 环境管理的复杂度


2. 本地线程 vs Java 子线程的数量对比

线程类型数量限制管理方式
Java 子线程受线程池配置限制(默认约 20)由 JVM 统一管理
本地线程(Native)仅受系统资源限制需开发者手动管理

3. 如何正确创建多个本地线程?

示例代码(C++11)
#include <thread>
#include <vector>JavaVM* g_jvm; // 全局保存 JVMvoid nativeWorkerTask(JNIEnv* env, int threadId) {// 1. 执行任务(如计算)// 2. 可通过 env 回调 Java
}void startMultipleThreads(JNIEnv* env, int threadCount) {std::vector<std::thread> threads;for (int i = 0; i < threadCount; i++) {threads.emplace_back([i] {// 附加当前线程到 JVMJNIEnv* env;g_jvm->AttachCurrentThread(&env, nullptr);// 执行任务nativeWorkerTask(env, i);// 分离线程g_jvm->DetachCurrentThread();});}// 让线程独立运行(不阻塞当前线程)for (auto& t : threads) {t.detach();}
}
关键注意事项
  1. JNI 环境管理

    • 每个本地线程必须 单独调用 AttachCurrentThread 获取 JNIEnv

    • 线程退出前必须 DetachCurrentThread,否则内存泄漏。

  2. 资源控制
    建议使用 线程池模式(如 C++ 的 std::async 或第三方库)。

  3. 线程安全

    • 共享数据需加锁(如 std::mutex)。

    • 避免在回调 Java 时持有锁,可能引发死锁。


4. 实际应用场景

适合多本地线程的情况
  • 音视频编解码(FFmpeg)

  • 并行图像处理(OpenCV)

  • 物理仿真计算

不适合的情况
  • 简单异步任务(用 Java 线程或协程更高效)

  • 需要频繁与 UI 交互的任务(应通过主线程 Handler 转发)


5. 性能优化建议

  1. 限制线程数量

    // 根据 CPU 核心数动态控制
    unsigned int maxThreads = std::thread::hardware_concurrency();
  2. 使用线程池

    #include <future>
    std::vector<std::future<void>> futures;
    futures.push_back(std::async(std::launch::async, []{// 任务代码
    }));
  3. 监控资源占用

    • 通过 /proc/self/status 查看线程数(Linux 系统)

    • 使用 Android Profiler 检测内存和 CPU 使用率


总结

  • 可以创建多个本地线程,但需手动管理生命周期和 JNI 环境。

  • 优先使用线程池,避免无限制创建线程。

  • 简单任务用 Java 线程,复杂计算再用本地线程。

http://www.dtcms.com/a/279429.html

相关文章:

  • Axure RP Extension for Chrome插件安装使用
  • 在 Ubuntu 上安装 vLLM:从 GPU 到 CPU 的三种方案
  • Oracle根据一张表的字段更新另一张表中的数据
  • Android 自定义路由系统
  • ServiceLibrary 库使用演示
  • [AI8051U入门第一步]环境安装和按键控制流水灯
  • 将dist文件打包成exe可执行程序
  • MySQL服务故障分析报告​​
  • 以楼宇自控系统为抓手,实现人居环境优化与建筑能效跃升
  • 职业教育领域的“101计划
  • keepalive模拟操作部署
  • 学习日志09 python
  • 【SVN】SVN 客户端的安装与操作
  • 设计模式之代理模式:掌控对象访问的优雅之道
  • CVE-2017-7525源码分析与漏洞复现(Jackson 反序列化)
  • Android 中 实现格式化字符串
  • vue2/3生命周期使用建议
  • TCL在芯片设计与验证中的应用实践
  • WinUI3开发_Combobox实现未展开时是图标下拉菜单带图标+文字
  • ConcurrentHashMap 原子操作详解:computeIfAbsent、computeIfPresent和putIfAbsent
  • 技术人生——第12集:思想为王,功能在后
  • (5)LangGraph4j框架ReActAgent实现
  • mit6.5840-lab4C-Snapshot-25Summer
  • Java Stream流详解
  • 文心一言 4.5 开源深度剖析:中文霸主登场,开源引擎重塑大模型生态
  • C++11 std::is_permutation:从用法到原理的深度解析
  • 什么是延迟双删
  • 算法训练营day18 530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先
  • 通过 ip a 查看网络接口名
  • 【算法】贪心算法:摆动序列C++