EventBus原始碼解讀詳細註釋(2)MainThread執行緒模型分析
[EventBus原始碼分析(一):入口函式提綱挈領(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51802172)
[EventBus原始碼分析(二):register方法儲存事件的訂閱者列表(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51819508)
[EventBus原始碼分析(三):post方法釋出事件【獲取事件的所有訂閱者,反射呼叫訂閱者事件處理方法】(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51821143)
[EventBus原始碼分析(四):執行緒模型分析(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51832001)
[EventBus原始碼解讀詳細註釋(1)register的幕後黑手](http://blog.csdn.net/wangshihui512/article/details/50914817)
[EventBus原始碼解讀詳細註釋(2)MainThread執行緒模型分析](http://blog.csdn.net/wangshihui512/article/details/50934012)
[EventBus原始碼解讀詳細註釋(3)PostThread、MainThread、BackgroundThread、Async四種執行緒模式的區別](http://blog.csdn.net/wangshihui512/article/details/50935729)
[EventBus原始碼解讀詳細註釋(4)register時重新整理的兩個map](http://blog.csdn.net/wangshihui512/article/details/50938663)
[EventBus原始碼解讀詳細註釋(5)事件訊息繼承性分析 eventInheritance含義](http://blog.csdn.net/wangshihui512/article/details/50947102)
[EventBus原始碼解讀詳細註釋(6)從事件釋出到事件處理,究竟發生了什麼!](http://blog.csdn.net/wangshihui512/article/details/50949960)
EventBus的執行緒模型
為何要定義執行緒模型?因為在Android的中執行緒的使用有以下的限制:
1 主執行緒不能被阻塞,UI的更新位於主執行緒,耗時操作如網路處理在後臺執行緒
2 事件的傳送和處理可能會位於不同執行緒
通過使用EvenBus的執行緒模型,我們可以定義處理事件的執行緒模型。EventBus中有四種執行緒模型:PostThread,MainThread,BackgroundThread,Async,下面分別介紹之。
PostThread
預設的執行緒模型,事件釋出和接收在相同的執行緒,適合用於完成時間非常短的任務,以免阻塞UI。例:
// Called in the same thread (default)
public void onEvent(MessageEvent event) {
log(event.message);
}
MainThread
訂閱者的事件處理方法在主執行緒被呼叫,適合處理開銷小的事件,以免阻塞主執行緒。例:
// Called in Android UI's main thread
public void onEventMainThread(MessageEvent event) {
textField.setText(event.message);
}
BackgroundThread
訂閱者的事件處理在後臺被呼叫。若事件傳送執行緒位於主執行緒,EventBus將使用一個後臺執行緒來逐個傳送事件
// Called in the background thread
public void onEventBackgroundThread(MessageEvent event){
saveToDisk(event.message);
}
Async
事件處理位於一個單獨的執行緒,該執行緒往往既不是傳送事件的執行緒,也不是主執行緒。使用這個模型適用於事件傳送無需等待事件處理後才返回,適合處理耗時操作,如請求網路。EventBus內部使用執行緒池來管理多個執行緒。例:
// Called in a separate thread
public void onEventAsync(MessageEvent event){
backend.send(event.message);
}
接下來從原始碼的角度分析MainThread
EventBus有個私有方法postToSubscription,這個私有方法是如何呼叫的且聽下回分解,先不管
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { switch (subscription.subscriberMethod.threadMode) { case PostThread: invokeSubscriber(subscription, event); break; case MainThread: if (isMainThread) { invokeSubscriber(subscription, event); } else { mainThreadPoster.enqueue(subscription, event); } break;通過解析執行緒模型,如果是MainThread執行緒模型,並且當前執行緒就是主執行緒,那麼可以直接在當前主執行緒通過反射呼叫訂閱者的事件處理方法;
如果當前執行緒不是主執行緒呢?當然只能藉助Handler了,將此事件傳送到主執行緒處理。
mainThreadPoster是EventBus的一個屬性,型別為HandlerPoster
是這樣構造的
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);傳入的第二個引數是主執行緒的Looper,第三個參數列示主執行緒事件處理最大時間為10ms,超時將重新排程事件
下邊看下HandlerPoster的內部是幹什麼的
final class HandlerPoster extends Handler {
private final PendingPostQueue queue;
/*事件處理最大時間,超時重新排程*/
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive;
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
/*入佇列操作,傳入訂閱者和事件*/
void enqueue(Subscription subscription, Object event) {
/*將訂閱者和事件封裝成PendingPost*/
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
/** public final boolean sendMessage(Message msg)
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in handleMessage,
* in the thread attached to this handler.
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
/** public final Message obtainMessage()
* Returns a new android.os.Message Messagefrom the global message pool. More efficient than
* creating and allocating new instances. The retrieved message has its handler set to this instance
* (Message.target == this).
*/
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
/** native public static long uptimeMillis();
* Returns milliseconds since boot, not counting time spent in deep sleep.
* @return milliseconds of non-sleep uptime since boot.
*/
/*記錄迴圈開始的時間*/
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;
}
}
}
這個類主要實現了一個在UI執行緒也就是主執行緒迴圈處理事件的功能,同時如果事件處理超時,將被重新排程。順便看下EventBus的資料結構是如何設計的
先看訊息佇列:
final class PendingPostQueue {
private PendingPost head;
private PendingPost tail;
/*入佇列操作*/
synchronized void enqueue(PendingPost pendingPost) {
/*良好程式設計習慣,入口引數檢查*/
if (pendingPost == null) {
throw new NullPointerException("null cannot be enqueued");
}
/*尾部不是空指標,佇列有尾部,後邊加一個PendingPost資料,該資料指標域已初始化為空指標*/
if (tail != null) {
tail.next = pendingPost;
tail = pendingPost;
/*佇列頭部為空指標,那麼肯定沒有頭部,也沒有尾部,佇列為空,把入隊的新元素作為頭部和尾部*/
} else if (head == null) {
head = tail = pendingPost;
/*佇列有頭無尾丟擲異常*/
} else {
throw new IllegalStateException("Head present, but no tail");
}
/**
* Causes all threads which are waiting on this object's monitor (by means
* of calling one of the code wait() methods) to be woken up. The threads
* will not run immediately. The thread that called code notify() has to
* release the object's monitor first. Also, the threads still have to
* compete against other threads that try to synchronize on the same object.
*This method can only be invoked by a thread which owns this object's
* monitor. A thread becomes owner of an object's monitor
* by executing a synchronized method of that object;
* by executing the body of a code synchronized statement that synchronizes on the object;
* by executing a synchronized static method if the object is of type code Class
* 1)wait()、notify()和notifyAll()方法是本地方法,並且為final方法,無法被重寫。
2)呼叫某個物件的wait()方法能讓當前執行緒阻塞,並且當前執行緒必須擁有此物件的monitor(即鎖)
3)呼叫某個物件的notify()方法能夠喚醒一個正在等待這個物件的monitor的執行緒,
如果有多個執行緒都在等待這個物件的monitor,則只能喚醒其中一個執行緒;
4)呼叫notifyAll()方法能夠喚醒所有正在等待這個物件的monitor的執行緒;
呼叫某個物件的wait()方法,當前執行緒必須擁有這個物件的monitor(即鎖),
因此呼叫wait()方法必須在同步塊或者同步方法中進行(synchronized塊或者synchronized方法)
呼叫某個物件的wait()方法,相當於讓當前執行緒交出此物件的monitor,然後進入等待狀態,
等待後續再次獲得此物件的鎖(Thread類中的sleep方法使當前執行緒暫停執行一段時間,從而讓其他執行緒有機會繼續執行,但它並不釋放物件鎖);
notify()方法能夠喚醒一個正在等待該物件的monitor的執行緒,當有多個執行緒都在等待該物件的monitor的話,
則只能喚醒其中一個執行緒,具體喚醒哪個執行緒則不得而知。
同樣地,呼叫某個物件的notify()方法,當前執行緒也必須擁有這個物件的monitor,
因此呼叫notify()方法必須在同步塊或者同步方法中進行(synchronized塊或者synchronized方法)。
nofityAll()方法能夠喚醒所有正在等待該物件的monitor的執行緒,這一點與notify()方法是不同的。
這裡要注意一點:notify()和notifyAll()方法只是喚醒等待該物件的monitor的執行緒,並不決定哪個執行緒能夠獲取到monitor。
一個執行緒被喚醒不代表立即獲取了物件的monitor,只有等呼叫完notify()或者notifyAll()並退出synchronized塊,
釋放物件鎖後,其餘執行緒才可獲得鎖執行。
*/
notifyAll();
}
/*隊首出佇列*/
synchronized PendingPost poll() {
PendingPost pendingPost = head;
if (head != null) {
/*隊首出佇列,將原佇列第二個資料作為新的隊首*/
/*注意原佇列只有一個資料的情況,隊首==隊尾都不是null,現在若隊首出佇列,隊尾應該設定null*/
head = head.next;
if (head == null) {
tail = null;
}
}
/*佇列為空,返回空指標*/
return pendingPost;
}
/*如果佇列為空,掛起當前執行緒,知道有新的資料加入佇列(入佇列操作後呼叫了notifyall() )*/
synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
if (head == null) {
/**
* Causes the calling thread to wait until another thread calls the
* notify() or notifyAll()method of this object or until the
* specified timeout expires. This method can only be invoked by a thread
* which owns this object's monitor;
* While the thread waits, it gives up ownership of this object's
* monitor. When it is notified (or interrupted), it re-acquires the monitor
* before it starts running.
* A timeout of zero means the calling thread should wait forever unless interrupted or
* notified.
* wait():
等待物件的同步鎖,需要獲得該物件的同步鎖才可以呼叫這個方法,
否則編譯可以通過,但執行時會收到一個異常:IllegalMonitorStateException。
呼叫任意物件的 wait() 方法導致該執行緒阻塞,該執行緒不可繼續執行,並且該物件上的鎖被釋放。
notify():
喚醒在等待該物件同步鎖的執行緒(只喚醒一個,如果有多個在等待),注意的是在呼叫此方法的時候,並不能確切的喚醒某一個等待狀態的執行緒,
而是由JVM確定喚醒哪個執行緒,而且不是按優先順序。
呼叫任意物件的notify()方法則導致因呼叫該物件的 wait()方法而阻塞的執行緒中隨機選擇的一個解除阻塞(但要等到獲得鎖後才真正可執行)。
notifyAll():
喚醒所有等待的執行緒,注意喚醒的是notify之前wait的執行緒,對於notify之後的wait執行緒是沒有效果的。
*/
wait(maxMillisToWait);
}
/*獲得物件鎖後返回隊首資料*/
return poll();
}
}
再看訊息類:
final class PendingPost {
/*開一個物件池,靜態ArrayList實現*/
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;
}
/*傳入事件和訂閱者,返回一個封裝好的PendingPost*/
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
/*對物件池加鎖*/
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
/*如果物件池儲存的有備胎物件,就用備胎物件,節省一次new物件*/
if (size > 0) {
/*從池子裡邊拿出來一個PendingPost*/
/* * Removes the object at the specified location from this code List.
* @param location the index of the object to remove.
* @return the removed object.
* 從池子也就是list拿出來一個PendingPost*/
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);
}
}
}
}
相關文章
- EventBus原始碼解讀詳細註釋(3)PostThread、MainThread、BackgroundThread、Async四種執行緒模式的區別原始碼threadAI執行緒模式
- EventBus原始碼解讀詳細註釋(1)register的幕後黑手原始碼
- EventBus原始碼解讀詳細註釋(5)事件訊息繼承性分析 eventInheritance含義原始碼事件繼承
- EventBus原始碼分析(四):執行緒模型分析(2.4版本)原始碼執行緒模型
- Netty原始碼分析之Reactor執行緒模型詳解Netty原始碼React執行緒模型
- EventBus原始碼解讀詳細註釋(4)register時重新整理的兩個map原始碼
- EventBus原始碼解讀詳細註釋(6)從事件釋出到事件處理,究竟發生了什麼!原始碼事件
- EventBus詳解及原始碼分析原始碼
- Dubbo RPC執行緒模型 原始碼分析RPC執行緒模型原始碼
- 詳解Java執行緒池的ctl(執行緒池控制狀態)【原始碼分析】Java執行緒原始碼
- Laravel 原始碼方法執行類詳細分析Laravel原始碼
- 【UGUI原始碼分析】Unity遮罩之RectMask2D詳細解讀UGUI原始碼Unity遮罩
- EventBus 3.0+ 原始碼詳解(史上最詳細圖文講解)原始碼
- 執行緒池執行模型原始碼全解析執行緒模型原始碼
- 執行緒池原始碼分析執行緒原始碼
- Bootstrap的Model原始碼詳細註釋 (轉)boot原始碼
- 【UGUI原始碼分析】Unity遮罩之Mask詳細解讀UGUI原始碼Unity遮罩
- dubbo原始碼-執行緒池分析原始碼執行緒
- 執行緒封閉之ThreadLocal原始碼詳解執行緒thread原始碼
- POSIX執行緒詳解(2) (轉)執行緒
- EventBus原始碼分析原始碼
- 詳細解讀:遠端執行緒注入DLL到PC版微信執行緒
- webrtc執行緒模型分析Web執行緒模型
- RxJava 執行緒模型分析RxJava執行緒模型
- netty原始碼分析-執行緒池Netty原始碼執行緒
- quartz執行緒管理的原始碼分析quartz執行緒原始碼
- 程式、執行緒詳細梳理執行緒
- Netty原始碼解析一——執行緒池模型之執行緒池NioEventLoopGroupNetty原始碼執行緒模型OOP
- Tomcat執行緒模型 BIO模型原始碼與調優Tomcat執行緒模型原始碼
- Guava 原始碼分析之 EventBus 原始碼分析Guava原始碼
- Linuxepoll模型詳解及原始碼分析Linux模型原始碼
- 深入併發之(四) 執行緒池詳細分析執行緒
- EventBus 原始碼分析(上篇)原始碼
- EventBus 3.0 原始碼分析原始碼
- 原始碼分析一:EventBus原始碼
- MySQL explain執行計劃詳細解釋MySqlAI
- 執行緒池之ThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記
- 執行緒池之ScheduledThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記