多线程详解——HandlerThread工作原理(源码详解)

目录

一、HandlerThread简介

1. HandlerThread产生背景

2. HandlerThread 的特点

二、HandlerThread用法

1、通过callback创建

2、通过Looper创建

3、举例

三、HandlerThread生命周期管理

四、HandlerThread API及源码分析

run方法

getLooper()方法

quit和quitSafely方法


一、HandlerThread简介

1. HandlerThread产生背景

开启Thread子线程进行耗时操作

多次创建和销毁线程是很耗系统资源的。

(为什么呢,因为java的线程是依托操作系统,线程的创建和销毁以及频繁切换都涉及到用户态和内核态的切换,以及线程上下文的保存和恢复,所以是比较耗资源的。)

既然已经有Handler可以实现线程间通信,为什么又设计了HandlerThread?

HandlerThread通过字面意思我们可以看到,它是Handler+Thread,那么我们猜测它应该实现了Handler和Thread功能,到底是不是呢,我们向下看。

首先,HandlerThread继承了Thread类,也就是说HandlerThread可以创建一个新的线程。

其次,HandlerThread内封装了Handler类,并自动创建了Looper和MessageQueue,也就是说我们使用HandlerThread线程时,不需要手动创建Looper。

优点:

之前我们使用Handler时,必须自己创建线程,并且自己创建Looper等相关的功能,而HandlerThread则提供了一种方便的方式,相当于内部已经集成了这些功能,不需要我们再手动创建Looper。

在Android开发中,通常在主线程(也称为UI线程)中执行耗时操作会导致界面卡顿甚至ANR(Application Not Responding)错误。为了避免这种情况,可以使用HandlerThread。

HandlerThread是一个带有Looper的线程,可以用来处理消息队列中的消息。通过HandlerThread,可以在后台线程执行耗时操作,然后通过Handler将结果发送回主线程更新UI,从而提高应用的性能和流畅度。

使用HandlerThread的好处包括:

  1. 避免在主线程执行耗时操作导致界面卡顿
  2. 可以轻松地在后台线程执行网络请求、数据库操作等耗时任务
  3. 可以方便地进行线程间的通信和数据传递
  4. 可以更好地控制线程的生命周期和执行顺序

总之,使用HandlerThread可以帮助提高应用的性能和用户体验,是Android开发中常用的线程处理方式之一。

2. HandlerThread 的特点

本质上就是封装了Handler的线程

  1. HandlerThread 本质是一个线程类,它继承自Thread
  2. HandlerThread 有自己的内部looper对象,可以进行loop循环
  3. 通过HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务
  4. 不会阻塞,减少了对性能的消耗,缺点是不能同时进行多任务的处理,需要等待进行,效率低。

这里的不会阻塞,指的是非UI线程,又想使用消息机制的那些操作。通过HandlerThread来执行是最合适的。

     5.与线程池并发不同,HandlerThread是一个串行队列,HandlerThread只有一个线程。

HandlerThread是一个轻量级的异步类,可以实现多线程,并且可以实现线程间的通信(HandlerThread主要应用是实现主线程到子线程的通信,子线程到主线程通信可以通过Handler机制)。

HandlerThread继承自Thread,内部实现了初始化了Looper,并创建了消息队列,接着调用了Looper.loop()开启了消息循环,这样HandlerThread就可以处理通过Handler传递过来的Message了,因为HandlerThread中的run方法是无限循环,当有消息过来时处理消息,没有消息时就会阻塞。当明确不需要HandlerThread时,可以调用quit或者quitSafely (API 18以上使用)来尝试终止线程。

二、HandlerThread用法

1、通过callback创建

    1.创建HandlerThread
    HandlerThread handlerThread = new HandlerThread("handlerThread_name");
 
    //2.必须先开启线程
    handlerThread.start();
 
    /**
     * 3.设置callback
     * 该callback运行于子线程
     */
    class ChildCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {
            //在子线程中进行相应的网络请求
 
            //通知主线程去更新UI
            mUIHandler.sendMessage(msg1);
            return false;
        }
    }
 
    //4.子线程Handler
    Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());

2、通过Looper创建

使用步骤:
//1、创建HandlerThread对象,即创建了一个新的线程,参数为线程名,仅仅是标记线程用的
HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");
//2、开启线程,第一步创建了一个新的线程,此处开启线程。
mHandlerThread.start();
//3、创建Handler,并重写handleMessage()方法
    //new Handler(mHandlerThread.getLooper()),即把该Handler绑定到mHandlerThread线程的Looper,
    //进而绑定到了线程mHandlerThread
Handler mHandlerInHandlerThread = new Handler(mHandlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
 	        //对信息的相关处理操作               
                //在子线程mHandlerThread中运行   
                super.handleMessage(msg);
            }
        };
//4、创建消息并发送消息
    //在主线程中
Message msg = Message.obtain();
//主线程向子线程mHandlerThread发送消息通信
mHandlerInHandlerThread.sendMessage(msg);
//5、结束线程。之前开启线程,当工作结束不再使用该线程时,应该结束该线程
    //即停止了线程的消息循环
    mHandlerThread.quit();

通过使用步骤我们可以看到,HandlerThread实现的功能主要就是主线程向子线程通信,另外可以在使用Handler实现子线程到主线程的通信,进而就可以实现主线程到子线程间的双向通信。

3、举例

handlerThread = new HandlerThread("handler_thread");
handlerThread.start();
//在主线程初始化,传入的是HandlerThread中的Looper
workHandler = new Handler(handlerThread.getLooper()) {
     @Override
     public void handleMessage(Message msg) {
         //Handler是在HandlerThread所在的子线程线程中处理Message
         switch (msg.what) {
             case MSG_UPDATE:
                 if (isStop) return;
                 try {
                     String result = String.valueOf(new Random().nextInt(9000) + 1000);
                     //通过uiHandler对象传送消息到主线程,更新UI
                     sendMsg(UI_UPDATE, result, uiHandler);
                     //延迟2秒
                     Thread.sleep(2000);
                     //循环发送消息
                     workHandler.sendEmptyMessage(MSG_UPDATE);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 break;
         }
     }
 };

首先,初始化HandlerThread并通过handlerThread.getLooper()关联一个在UI线程初始化的workHandler,这样就可以通过workHandler在主线程中向HandlerThread中发送消息了,这里要注意一下,workHandler 中的handleMessage()方法是在HandlerThread子线程中执行的,为什么会在子线程中执行呢?

因为在初始化workHandler 时传入的是HandlerThread的Looper,而workHandler 是把消息发送到HandlerThread中去,HandlerThread在执行Looper.loop()方法后会循环取出消息并处理消息,所以workHandler 中的handleMessage()方法是在HandlerThread子线程中执行的,那么处理完消息后怎么更新到UI线程呢?

//在主线程初始化,传入的是主线程中的Looper
 uiHandler = new Handler(Looper.getMainLooper()) {
     @Override
     public void handleMessage(Message msg) {
         //在主线程中处理Message
         switch (msg.what) {
             case UI_UPDATE:
                 //更新验证码
                 String result = (String) msg.obj;
                 tv_random.setText(result);
                 break;
         }
     }
 };

我们在主线程中初始化了另一个uiHandler 并传入了主线程的Looper,所以此时最后处理消息的回调方法handleMessage()是在主线程中执行的,当HandlerThread处理完逻辑后,通过uiHandler把结果发到主线程中,就可以愉快地来更新UI了,最后别忘了关闭线程:

@Override
 protected void onDestroy() {
     if (SDK_INT >= 18) {
         handlerThread.quitSafely();
     } else {
         handlerThread.quit();
     }
     super.onDestroy();
 }

三、HandlerThread生命周期管理

使用HandlerThread完成任务后,要记得调用quit方法退出线程。避免出现内存泄漏问题!!!

四、HandlerThread API及源码分析

run方法

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {//同步代码块,确保同一时刻只有一个线程可以访问mLooper
            mLooper = Looper.myLooper();
            notifyAll();//通知当前等待线程,主要是getLooper()方法里的wait方法
        }
        Process.setThreadPriority(mPriority);//设定线程优先级
        onLooperPrepared();//空方法,留个用户自己重写
        Looper.loop();//初始化数据接收,开启循环。
        mTid = -1;
    }

getLooper()方法

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
 
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {//同步代码块,如果looper没有创建成功,就一直阻塞,直到looper创建完毕。
            while (isAlive() && mLooper == null) {
                try {
                    //这个是没有设置TimeOut的Object:wait方法,无限期等待,必须是其他线程调用notify或者notifyAll才能唤醒。
                    //当run方法的mLooper对象创建完毕时,就会调用notifyAll方法唤醒。
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

quit和quitSafely方法

    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
 
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

quitsafely比quit更安全,各自的实现原理如下。

    //Looper.java
    public void quit() {
        mQueue.quit(false);
    }
    public void quitSafely() {
        mQueue.quit(true);
    }
 
    //MessageQueue.java
    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
 
        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;
 
            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }
 
            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }
 
    private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }
 
    private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }

推荐文章

HandlerThread原理、使用实例、源码详细解析-优快云博客

https://zhuanlan.zhihu.com/p/424513321

HandlerThread相关问题总结 - cfdroid - 博客园

为什么要使用handlerthread - 问答 - 亿速云

<think>我们正在解决一个关于threadsafe_queue.cpp文件中成员函数wait_and_pop出现的错误:'operator()'被声明为返回函数的错误。 根据错误信息,这通常是由于函数声明或定义的语法问题导致的。例如,可能将函数声明为返回一个函数类型,而不是返回一个值。 在C++中,函数不能直接返回一个函数,但可以返回函数指针或函数对象(如lambda表达式)。因此,错误可能是由于在声明返回类型时使用了不正确的语法。 参考引用[1]中提到的,wait_and_pop函数通常与条件变量一起使用,使用std::unique_lock来等待条件满足。 典型的wait_and_pop函数有两种形式: 1. 第一种:传入一个引用参数来存储弹出的值,并返回void。 2. 第二种:返回一个值(通过构造一个shared_ptr或者直接返回值)。但要注意,返回值的版本可能会在拷贝/移动时抛出异常,因此需要谨慎。 我们来看一下可能出错的代码模式: 错误示例1:试图将函数作为返回类型 auto wait_and_pop() -> void(); // 错误:这里声明返回一个函数类型,该函数返回void,而不是返回void 错误示例2:在函数声明中误用了括号 void wait_and_pop(T& value)() { ... } // 错误:多了一对括号,导致声明了一个函数返回函数类型 正确声明应该是: void wait_and_pop(T& value); 或者 T wait_and_pop(); 另外,如果采用返回智能指针的方式: std::shared_ptr<T> wait_and_pop(); 因此,我们需要检查threadsafe_queue.hpp(或.cpp)中wait_and_pop的声明和定义,确保语法正确。 根据引用[1]中的描述,wait_and_pop的实现通常如下: 版本1(传入引用): void wait_and_pop(T& value) { std::unique_lock<std::mutex> lk(mut); data_cond.wait(lk, [this]{return !data_queue.empty();}); value = data_queue.front(); data_queue.pop(); } 版本2(返回shared_ptr): std::shared_ptr<T> wait_and_pop() { std::unique_lock<std::mutex> lk(mut); data_cond.wait(lk, [this]{return !data_queue.empty();}); std::shared_ptr<T> res(std::make_shared<T>(data_queue.front())); data_queue.pop(); return res; } 所以,错误可能是由于在声明或定义函数时,不经意间在函数名后面多了一个括号,导致编译器认为我们在声明一个返回函数的函数。 请检查你的代码中wait_and_pop的声明和定义。例如: 错误的声明: void wait_and_pop(T& value)(); // 多了一对括号 正确的声明: void wait_and_pop(T& value); // 没有多余的括号 或者,如果你使用的是返回shared_ptr的版本: std::shared_ptr<T> wait_and_pop(); // 注意:这里没有多余的括号 另外,也要检查成员函数定义的语法。定义时,函数名后面只有参数列表,不应该再有额外的括号。 例如,定义应该是: void threadsafe_queue<T>::wait_and_pop(T& value) { // ... } 而不是: void threadsafe_queue<T>::wait_and_pop(T& value)() { // 错误:多了一对括号 // ... } 因此,请仔细检查函数声明和定义处的语法,确保没有多余的括号。 如果问题仍然存在,请提供出错的代码片段,以便更准确地定位问题。 注意:根据引用[2],你可能还面临一些环境兼容性问题,但当前错误是编译错误,与环境兼容性问题不同。 总结:检查wait_and_pop函数的声明和定义,确保没有多余的括号,并且返回类型正确。 </think>### 解决 `threadsafe_queue.cpp` 中 `wait_and_pop` 成员函数的错误 错误 `'operator()' 被声明为返回函数` 通常是由于 **Lambda表达式语法错误** 或 **函数声明歧义** 导致的。以下是具体解决方案: --- ### 错误原因分析 1. **Lambda表达式错误** `std::condition_variable::wait` 的谓词参数(第二个参数)必须是**可调用对象**,但可能被错误声明为返回函数类型: ```cpp // ❌ 错误示例:Lambda 的返回类型声明错误 data_cond.wait(lk, [](){ return !data_queue.empty(); }); // 若写成 []()() { ... } 会导致返回函数类型 ``` 2. **成员函数声明歧义** 函数声明时误用 `operator()` 语法: ```cpp // ❌ 错误示例:声明了一个返回函数的函数 void wait_and_pop()(); // 多出的一对括号导致歧义 ``` --- ### 修复步骤(两种常见实现) #### 方案一:传入引用参数(推荐) ```cpp void wait_and_pop(T& value) { // ✅ 正确声明 std::unique_lock<std::mutex> lk(mut); // ✅ 正确的Lambda表达式:返回布尔值 data_cond.wait(lk, [this] { return !data_queue.empty(); }); value = data_queue.front(); data_queue.pop(); } ``` **关键点修正**: 1. 移除Lambda中多余的 `()` 错误形式 `[this]() { ... }` 可能被误写为 `[this]()() { ... }` 2. 确保函数声明无多余括号 正确形式为 `wait_and_pop(T& value)`,而非 `wait_and_pop(T& value)()` #### 方案二:返回智能指针 ```cpp std::shared_ptr<T> wait_and_pop() { // ✅ 正确声明 std::unique_lock<std::mutex> lk(mut); data_cond.wait(lk, [this] { return !data_queue.empty(); }); // ✅ Lambda auto res = std::make_shared<T>(data_queue.front()); data_queue.pop(); return res; } ``` --- ### 错误排查清单 1. **检查Lambda表达式格式** - 确保为 `[capture] { ... }` 或 `[capture]() { ... }` - 禁止出现嵌套括号如 `[capture]()() { ... }` 2. **校验成员函数声明** - 函数签名后禁止追加 `()`: 正确:`void wait_and_pop(T&)` 错误:`void wait_and_pop(T&)()` 3. **验证头文件依赖** - 包含必要头文件: ```cpp #include <mutex> #include <condition_variable> #include <queue> ``` --- ### 底层机制解析 `std::condition_variable::wait` 的工作流程[^1]: 1. 检查谓词条件(如 `!data_queue.empty()`) 2. 若条件不满足,**释放锁**并挂起线程 3. 被唤醒后**重新获取锁**,再次检测条件 4. 条件满足则继续执行 此过程需配合 `std::unique_lock`(而非 `std::lock_guard`),因其支持灵活的锁管理[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲暇部落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值