EventBus 源码关于 BackgroundPoster 的疑惑

2019-08-04 14:57:43 +08:00
 maninfog

源码如下:

 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从线程池中抽取了一个单独线程,循环读取队列中的PendingPost执行。那么但是既然是单线程,run()方法中的加锁的目的在哪里呢?我感觉完全可以去除加锁代码,简写成如下的代码:

public void run() {
        try {
            try {
                while (true) {
                    PendingPost pendingPost = queue.poll(1000);
                    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;
        }
    }

我是在哪方面没有考虑到呢?

8926 次点击
所在节点    Android
4 条回复
shily
2019-08-05 10:08:11 +08:00
enqueue 方法是其他线程调用的啊,大兄弟。
ChenFanlin
2019-08-05 10:49:21 +08:00
EventBusBuilder#DEFAULT_EXECUTOR_SERVICE 是 Executors.newCachedThreadPool();
然后这个 BackgroundPoster 是加了个变量`executorRunning`来使他同时只在一个线程中运行,两个锁是确保`executorRunning`的值不出错,两个加锁的代码块同时只能执行一个
假如去掉了下面一个, 假设一种情况, 如果队列里是空的, 先执行`pendingPost = queue.poll();` 取到 null, **executorRunning 还未赋值为 false 的时候** , 这时候`queue.enqueue()`进去了,
```java
if (!executorRunning) {
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
```
这段代码就不会执行, 就得被推迟到下一次 background 的分发了
**一只小菜鸡, 请轻喷**
ChenFanlin
2019-08-05 10:53:56 +08:00
..原来回复不支持 md 啊..
maninfog
2019-08-05 11:06:05 +08:00
@ChenFanlin #3 自己研究了下加上你的解释明白啦 感谢

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/588928

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX