EventBus有多种发送消息的模式,都有各自的消息队列,由各自的线程遍历队列,然后由一个线程池统一管理。这里以BACKGROUND为例,介绍一下。
final class BackgroundPoster implements Runnable, Poster {
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
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);
}
}
}
@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) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
BackgroundPoster是BACKGROUND模式对应的线程,他有一个队列PendingPostQueue。队列是一种先进先出的存储结构,每一个存储数据对应一个节点,节点有一个next属性,用于指向下一个节点,这样所有的数据被串成一个队列。PendingPostQueue提供一个头节点head和一个尾节点用于管理数据。
入列enqueue时,尾节点的next指向被新增元素,新增元素就在队尾(不考虑为空的特殊情况)
tail.next = temp; // 新增元素与队列串起来,串在最后
tail = temp;
出列poll时,首节点指向原首节点的next,这样就与队列断开了。
temp = head;
head = head.next; // head与串断开
retrun temp;
这样就保证了队列的先进先出。
再看BackgroundPoster的run(),有“while(true)”,死循环的写法,在不停的从队列取出消息,但又没有影响程序的运行,所以一定存在着对CPU的释放。看这一句,queue.poll(1000),其源码如下:
synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
if (head == null) {
wait(maxMillisToWait);
}
return poll();
}
wait()会释放CPU和锁。CPU被释放后,才能去执行进程的其他任务,所以这种死循环的写法也不会影响程序的运行。我之前一直不明白释放CPU的意义。wait超时结束后,重新获取一次出列元素。如果依然取得为null,则会return掉,终止线程的运行。
但是入列时,判断线程如果不是运行状态,会用线程池重新运行起来:eventBus.getExecutorService().execute(this);。
我以前是纯小白时,不想新建线程对象,用过如下的写法:
Runable r = new Runnable(){...};
r.start();
r.start();
毫无悬念,第二个start()会产生崩溃,提示线程已经结束。但是换成用线程池执行的方式,像这样写2次,就是OK的。这是我对线程池的优势的体会和理解。