EventBus -2,所用的队列、线程池

441 阅读2分钟

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的。这是我对线程池的优势的体会和理解。