深入理解HandlerThread

深入理解HandlerThread以往遇到HandlerThread,对它的认识只是停留在MessageLooperHandler上,知道它有自己的消息队列,仅此而已。随着编程的深入,个人已不再满足表面上的理解,所以再次翻开HandlerThread源码,做梳理记录。HandlerThread集成Thread,并重写了Thread类的run方法(如果我们自定义一个类继承HandlerThread,就用不到run函数了):

大家好,又见面了,我是你们的朋友全栈君。

以往遇到HandlerThread,对它的认识只是停留在Message<->Looper<->Handler上,知道它有自己的消息队列,仅此而已。随着编程的深入,表面上的理解无法满足要求,所以再次翻开HandlerThread源码,做梳理记录。

HandlerThread集成Thread,并重写了Thread类的run方法(如果我们自定义一个类继承HandlerThread,就用不到run函数了):

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

从图中我们可以看到,HandlerThread的run方法先后执行了Looper.prepare()、Looper.loop(),因为我们知道这样做的目的是为了给HandlerThread实例建立一个属于它的消息队列(MessageQueue,这一点像极了Windows系统的窗口消息分发机制)。

     /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

Looper.loop()的工作就是开始不断的从消息队列中取出消息(在loop函数里有个for循环,除非线程被系统杀死或者被调用quit函数,负责for循环会一直执行)

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    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();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

有人说Looper中的sThreadLocal属性(ThreadLocal<Looper>类型)是个静态常量,怎么保证每个执行过Looper.prepare()、Looper.loop()的线程都有唯一的Looper呢?这个问题刚开始我也很好奇,后来发现原因在ThreadLocal类。

/**
 * Implements a thread-local storage, that is, a variable for which each thread
 * has its own value. All threads share the same {@code ThreadLocal} object,
 * but each sees a different value when accessing it, and changes made by one
 * thread do not affect the other threads. The implementation supports
 * {@code null} values.
 *
 * @see java.lang.Thread
 * @author Bob Lee
 */
public class ThreadLocal<T> 

上述类的解释是说ThreadLocal实现了线程的本地存储,即所有的线程共同使用同一个ThreadLocal对象,但每个线程都会有一个ThreadLocal对象副本,每个线程的ThreadLocal对象副本又是相互独立的,互不影响。

推荐一篇介绍ThreadLocal的博文,写的很不错:彻底理解ThreadLocal

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/157929.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • rabbitmq集群部署详解_搭建hadoop集群的步骤

    rabbitmq集群部署详解_搭建hadoop集群的步骤集群搭建参考文档RabbitMQ集群搭建了解原理参考文档RabbitMQ集群原理(非常简洁优秀的总结)1.保证各个节点erlang版本一致。RabbitMQ基于erlang语言,必须保证各个节点erlang版本完全一致。2.可以跨操作系统建立集群。因RabbitMQ基于erlang运行,因此只要erlang版本一致即可,与操作系统无关。3.如果节点加入集群失败,请根据给出的错误原因对症解决。一般有以下几点:a.连接磁盘节点失败。…

    2025年10月23日
    6
  • pycharm双击打不开_pycharm社区版双击没反应

    pycharm双击打不开_pycharm社区版双击没反应方法一:出现这个问题应该是想永久使用pycharm然后修改了pycharm的Help”->“EditCustomVMOptions文件,导致打不开pycharm,无论怎么下载重新下载还是打不开,原因就是我们所修改的文件,在最后一行添加路径的时候出现了错误。打开C盘用户名下的文件夹,按图中步骤找到pycharm,打开.vmoptions文件,删除最下面这一行自己添加的语句,。然后重启pycharm就可以了。方法二:我们可以在Windows10系统的开始菜单上,单击

    2022年8月27日
    6
  • node读取文件操作

    node读取文件操作导言:nodejs中所有与文件相关的操作都在fs模块中,而读写操作又是我们会经常用到的操作,nodejs的fs模块针对读操作为我们提供了readFile,read,createReadStream三个方法,针对写操作为我们提供了writeFile,write,createWriteStream三个方法,下面分析一下它们的区别:一、readFile和writeFile1、readFile…

    2022年5月29日
    51
  • 私有云的构建组成

    私有云的构建组成无论在公有云还是私有云中 你都无需去考虑底层基础设施 而只需要通过虚拟机和网络处理业务 当然 硬件在供应商那里 如果你正在构建一个私有云 会有很多选项来决定如何去构建它 每个选项都有不同的特性 安全性能和成本 但是在任何一种情况下 你都必须保留大量的安全责任 私有云这些选项与传统的服务器部署模式类似 你可以部署在自己的服务器上 也可以在一个联合本地中心部署 你甚至可以在 托管但是专用 的基础上

    2026年1月19日
    2
  • windows关闭/开启休眠命令行

    windows关闭/开启休眠命令行C:\hiberfil.sys占用空间过大,可以考虑关闭休眠重新开启

    2022年8月1日
    5
  • 计算机用户名起什么好,如何随机取名计算机名-如何改计算机用户名

    计算机用户名起什么好,如何随机取名计算机名-如何改计算机用户名如何改计算机用户名(administrator)3、右边找到“账户:重命名系统管理员账户”,鼠标右键,选择“性”。打开电脑,进入桌面。右击我的电脑,打开管理设置;打开系统工具,进入计算机管理页面,选择“本地用户和组”;在“全名”处输入想要的计算机用户名(可写描述)“我的电脑”,右键“管理”,这时会打开一个窗口”计算机管理“。双击”本地用户和组“,再”用户“,右面窗口中会看到”administrat…

    2022年10月14日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号