EventBus原始碼分析(四):執行緒模型分析(2.4版本)

王世暉發表於2016-07-05

EventBus原始碼分析(一):入口函式提綱挈領(2.4版本)
EventBus原始碼分析(二):register方法儲存事件的訂閱者列表(2.4版本)
EventBus原始碼分析(三):post方法釋出事件【獲取事件的所有訂閱者,反射呼叫訂閱者事件處理方法】(2.4版本)
EventBus原始碼分析(四):執行緒模型分析(2.4版本)
EventBus原始碼解讀詳細註釋(1)register的幕後黑手
EventBus原始碼解讀詳細註釋(2)MainThread執行緒模型分析
EventBus原始碼解讀詳細註釋(3)PostThread、MainThread、BackgroundThread、Async四種執行緒模式的區別
EventBus原始碼解讀詳細註釋(4)register時重新整理的兩個map
EventBus原始碼解讀詳細註釋(5)事件訊息繼承性分析 eventInheritance含義
EventBus原始碼解讀詳細註釋(6)從事件釋出到事件處理,究竟發生了什麼!

EventBus有四種執行緒模型

  1. PostThread模式不需執行緒切換,直接在釋出者執行緒進行事件處理。
  2. MainThread模式分類討論:釋出者執行緒是主執行緒則直接呼叫事件處理方法,否則通過Handler進行執行緒切換,切換到主執行緒處理事件,該模式下事件是序列執行的。
  3. BackgroundThread模式分類討論:釋出者執行緒不是主執行緒則在釋出者執行緒直接處理事件,否則執行緒切換至執行緒池處理,所有該執行緒模式下的事件會線上程池中用一個執行緒排隊序列處理(直到佇列裡邊的事件處理完之後又有新的事件釋出出來才會向執行緒池獲取一個新的執行緒)。
  4. Async模式不關心釋出者執行緒直接線上程池中開闢一個新的執行緒處理事件,和BackgroundThread模式不同的是,該執行緒模式的每個事件都是線上程池中開闢一個執行緒處理,事件之間併發處理,並不排隊。

PendingPostQueue待處理事件佇列

在分析執行緒模型之前,先看下各種執行緒模型都用到的資料結構PendingPostQueue,PendingPostQueue顧名思義是一個佇列,對待處理事件進行了排隊。

final class PendingPostQueue {
    private PendingPost head;
    private PendingPost tail;
    ...

PendingPostQueue維護了一個佇列連結串列,佇列的資料是PendingPost,PendingPost隨後分析。
既然PendingPostQueue是一個佇列,那麼肯定提供了出佇列和入佇列的操作。

    synchronized void enqueue(PendingPost pendingPost) {
        if (pendingPost == null) {
            throw new NullPointerException("null cannot be enqueued");
        }
        if (tail != null) {
            tail.next = pendingPost;
            tail = pendingPost;
        } else if (head == null) {
            head = tail = pendingPost;
        } else {
            throw new IllegalStateException("Head present, but no tail");
        }
        notifyAll();
    }

可見入佇列操作就是通過隊尾指標操作將新資料插入佇列尾部。如果隊尾tail不為null,說明佇列不空,將新資料作為新的隊尾,修改隊尾指標,如果隊尾tail和隊首head都是null,說明佇列為空,將新資料同時設為隊首和隊尾。
最後通過notifyAll()釋放物件鎖,那麼可以猜測如果佇列為空出佇列操作時會等待這把鎖。

    synchronized PendingPost poll() {
        PendingPost pendingPost = head;
        if (head != null) {
            head = head.next;
            if (head == null) {
                tail = null;
            }
        }
        return pendingPost;
    }

    synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
        if (head == null) {
            wait(maxMillisToWait);
        }
        return poll();
    }

出佇列操作就直接獲取佇列頭head即可,佇列為空時獲取的head是null。此時如果呼叫的是poll(int maxMillisToWait)則會等待maxMillisToWait時長的鎖,超時還沒有獲取鎖,也就是讀一個空佇列超時了還沒有入佇列操作,則呼叫poll(),這時會返回null。

PendingPost:不變模式和物件池模式

PendingPostQueue維護了一個PendingPost型別資料的佇列,那麼PendingPost是什麼呢?PendingPost是個物件池,通過靜態ArrayList實現。同時PendingPost是一個最終類,被final修飾,語義角度意味著不可被繼承,執行緒安全方面則表示該類絕對執行緒安全。

final class PendingPost {
    private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();

    Object event;
    Subscription subscription;
    PendingPost next;

    private PendingPost(Object event, Subscription subscription) {
        this.event = event;
        this.subscription = subscription;
    }

    static PendingPost obtainPendingPost(Subscription subscription, Object event) {
        synchronized (pendingPostPool) {
            int size = pendingPostPool.size();
            if (size > 0) {
                PendingPost pendingPost = pendingPostPool.remove(size - 1);
                pendingPost.event = event;
                pendingPost.subscription = subscription;
                pendingPost.next = null;
                return pendingPost;
            }
        }
        return new PendingPost(event, subscription);
    }

    static void releasePendingPost(PendingPost pendingPost) {
        pendingPost.event = null;
        pendingPost.subscription = null;
        pendingPost.next = null;
        synchronized (pendingPostPool) {
            // Don't let the pool grow indefinitely
            if (pendingPostPool.size() < 10000) {
                pendingPostPool.add(pendingPost);
            }
        }
    }
}

既然是個物件池模式,那麼必然提供了向物件池獲取物件的方法和物件使用結束向物件池歸還物件的方法。
PendingPost類通過將構造器設定為私有達到只能通過物件池獲取PendingPost物件的目的。
obtainPendingPost從物件池獲取物件,物件池中有儲存的物件則從池子中獲取物件(ArrayList尾部獲取),沒有儲存執行緒的物件的話就通過new建立。
releasePendingPost則將使用後的物件歸還給物件池,歸還的時候要將物件的使用痕跡擦除,同時要限制物件池大小為10000,防止物件池無限增大。

MainThread執行緒模式:HandlerPoster

MainThread執行緒模式模式由HandlerPoster實現,通過Handler的執行緒切換功能做到在主執行緒中處理事件。從名字就知道HandlerPoster也是一個Handler,是Handler的子類。

final class HandlerPoster extends Handler {

    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;
    ...

queue是待處理事件佇列
maxMillisInsideHandleMessage表示如果事件在maxMillisInsideHandleMessage時間內都沒有處理完成的話就需要重新排程(重新處理rescheduled = true)。

    void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

enqueue從PendingPost的物件池獲取一個PendingPost物件,設定subscription和event屬性後就加入PendingPostQueue佇列,如果主執行緒沒有事件要處理,就傳送一條空訊息,啟用handleMessage方法,在handleMessage方法裡邊對PendingPostQueue佇列裡邊的所有事件反射呼叫事件處理方法。

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            while (true) {
                PendingPost pendingPost = queue.poll();
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }
                eventBus.invokeSubscriber(pendingPost);
                long timeInMethod = SystemClock.uptimeMillis() - started;
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }

可知在handleMessage中時通過eventBus.invokeSubscriber(pendingPost)處理事件

    void invokeSubscriber(PendingPost pendingPost) {
        Object event = pendingPost.event;
        Subscription subscription = pendingPost.subscription;
        PendingPost.releasePendingPost(pendingPost);
        if (subscription.active) {
            invokeSubscriber(subscription, event);
        }
    }
    void invokeSubscriber(Subscription subscription, Object event) {
        try {
            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }

invokeSubscriber方法取出PendingPost物件的event和subscription後先將PendingPost物件歸還給執行緒池,然後反射呼叫事件處理方法。
MainThread執行緒模式總結:通過Handler從工作執行緒切換到主執行緒,在主執行緒中通過反射呼叫事件處理方法。

BackgroundThread執行緒模式:BackgroundPoster

BackgroundPoster繼承自Runnable,某一時段內BackgroundThread模式的事件都會在BackgroundPoster的run方法中排隊處理,也就是說該時段內的所有事件是在一個執行緒中排隊後序列執行的(佇列中的事件處理完之後又有新的事件釋出才會新開執行緒)。

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;
                eventBus.getExecutorService().execute(this);
            }
        }
    }

可以看到如果BackgroundPoster這個Runnable正在被執行緒池執行,這時候executorRunning==true,那麼在executorRunning==true情況下發布的事件只會進佇列,不會再次呼叫執行緒池的execute方法。這樣的話在BackgroundPoster覆寫的run方法中一定是通過死迴圈遍歷處理佇列中的事件,全部處理後才退出死迴圈,設定executorRunning=fasle,此後再發布事件才會線上程池中開闢一個新執行緒。

    @Override
    public void run() {
        try {
            try {
                while (true) {
                    PendingPost pendingPost = queue.poll(1000);
                    if (pendingPost == null) {
                        synchronized (this) {
                            // Check again, this time in synchronized
                            pendingPost = queue.poll();
                            if (pendingPost == null) {
                                executorRunning = false;
                                return;
                            }
                        }
                    }
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }

Async執行緒模式:AsyncPoster

AsyncPoster同樣也是一個Runnable,與Backgroundposter不同的是AsyncPoster併發度更高,不是在一個執行緒中對佇列中的事件進行序列處理,而是每一個新新增的任務都會線上程池中開闢一個新執行緒執行。

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        queue.enqueue(pendingPost);
        eventBus.getExecutorService().execute(this);
    }

    @Override
    public void run() {
        PendingPost pendingPost = queue.poll();
        if(pendingPost == null) {
            throw new IllegalStateException("No pending post available");
        }
        eventBus.invokeSubscriber(pendingPost);
    }

相關文章