不一样角度看Handler——另类三问

以前有一章节介绍了Handler的常见面试题,今天就来讲说另类的,可能你没关注的其余问题,一块儿看看吧。java

系统为何提供Handler

  • 这点你们应该都知道一些,就是为了切换线程,主要就是为了解决在子线程没法访问UI的问题。

那么为何系统不容许在子线程中访问UI呢?面试

  • 由于Android的UI控件不是线程安全的,因此采用单线程模型来处理UI操做,经过Handler切换UI访问的线程便可。

那么为何不给UI控件加锁呢?数组

  • 由于加锁会让UI访问的逻辑变得复杂,并且会下降UI访问的效率,阻塞线程执行。

Handler是怎么获取到当前线程的Looper的

  • 你们应该都知道Looper是绑定到线程上的,他的做用域就是线程,并且不一样线程具备不一样的Looper,也就是要从不一样的线程取出线程中的Looper对象,这里用到的就是ThreadLocal

假设咱们不知道有这个类,若是要完成这样一个需求,从不一样的线程获取线程中的Looper,是否是能够采用一个全局对象,好比hashmap,用来存储线程和对应的Looper?因此须要一个管理Looper的类,可是,线程中并不止这一个要存储和获取的数据,还有可能有其余的需求,也是跟线程所绑定的。因此,咱们的系统就设计出了ThreadLocal这种工具类。安全

ThreadLocal的工做流程是这样的:咱们从不一样的线程能够访问同一个ThreadLocal的get方法,而后ThreadLocal会从各自的线程中取出一个数组,而后再数组中经过ThreadLocal的索引找出对应的value值。具体逻辑呢,咱们仍是看看代码,分别是ThreadLocal的get方法和set方法:工具

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    } 
    
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }    
    
 	public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

首先看看set方法,获取到当前线程,而后取出线程中的threadLocals变量,是一个ThreadLocalMap类,而后将当前的ThreadLocal做为key,要设置的值做为value存到这个map中。oop

get方法就同理了,仍是获取到当前线程,而后取出线程中的ThreadLocalMap实例,而后从中取到当前ThreadLocal对应的值。性能

其实能够看到,操做的对象都是线程中的ThreadLocalMap实例,也就是读写操做都只限制在线程内部,这也就是ThreadLocal故意设计的精妙之处了,他能够在不一样的线程进行读写数据并且线程之间互不干扰。学习

画个图方便理解记忆:this

ThreadLocal.PNG

当MessageQueue 没有消息的时候,在干什么,会占用CPU资源吗。

  • MessageQueue 没有消息时,便阻塞在 loop 的 queue.next() 方法这里。具体就是会调用到nativePollOnce方法里,最终调用到epoll_wait()进行阻塞等待。

这时,主线程会进行休眠状态,也就不会消耗CPU资源。当下个消息到达的时候,就会经过pipe管道写入数据而后唤醒主线程进行工做。操作系统

这里涉及到阻塞和唤醒的机制叫作 epoll 机制

先说说文件描述符和I/O多路复用

在Linux操做系统中,能够将一切都看做是文件,而文件描述符简称fd,当程序打开一个现有文件或者建立一个新文件时,内核向进程返回一个文件描述符,能够理解为一个索引值。

I/O多路复用是一种机制,让单个进程能够监视多个文件描述符,一旦某个描述符就绪(通常是读就绪或写就绪),可以通知程序进行相应的读写操做

因此I/O多路复用其实就是一种监听读写的通知机制,而Linux提供的三种 IO 复用方式分别是:select、poll 和 epoll 。而这其中epoll是性能最好的多路I/O就绪通知方法。

因此,这里用到的epoll其实就是一种I/O多路复用方式,用来监控多个文件描述符的I/O事件。经过epoll_wait方法等待I/O事件,若是当前没有可用的事件则阻塞调用线程。

拜拜

今天就说这么多了,感兴趣的朋友也能够继续深究下去,好比epoll为何是性能最好的I/O多路复用方法?Handler在App启动流程中涉及到了哪些功能?等等。有机会再和你们聊聊~

有一块儿学习的小伙伴能够关注下❤️个人公众号——码上积木,天天剖析一个知识点,咱们一块儿积累知识。

相关文章
相关标签/搜索