不一樣角度看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啓動流程中涉及到了哪些功能?等等。有機會再和你們聊聊~

有一塊兒學習的小夥伴能夠關注下❤️個人公衆號——碼上積木,天天剖析一個知識點,咱們一塊兒積累知識。

相關文章
相關標籤/搜索