我有关于CommonsWare 5.9事件总线示例的线程安全问题/问题。
在我看来,从多个线程访问model
ArrayList
的方式存在问题。如果这样有效,我会很高兴理解为什么。
此代码执行model
的声明,初始化和填充。
private static final String[] items= { "lorem", "ipsum", "dolor",
"sit", "amet", "consectetuer", "adipiscing", "elit", "morbi",
"vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam",
"vel", "erat", "placerat", "ante", "porttitor", "sodales",
"pellentesque", "augue", "purus" };
private ArrayList<String> model=new ArrayList<String>();
private boolean isStarted=false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
if (!isStarted) {
isStarted=true;
new LoadWordsThread().start();
}
}
public ArrayList<String> getModel() {
return(model);
}
class LoadWordsThread extends Thread {
@Override
public void run() {
for (String item : items) {
if (!isInterrupted()) {
model.add(item);
EventBus.getDefault().post(new WordReadyEvent());
SystemClock.sleep(400);
}
}
}
}
每隔400毫秒醒来,并在model
添加一个条目。 model
的消费稍微复杂一点,因为我不容易将代码发布。它位于ArrayAdapter
内的某个地方。通过调用ArrayAdapter.notifyDataSetChanged()
来触发模型的消耗。
我遇到的问题是model
是在一个线程(而不是Android UI线程)中编写的,但消费是在UI线程上发生的。 model
ArrayList
的所有元素都是不可变的,这有所帮助,但它对我来说似乎并不合适。
如果此代码实际上是正确的,我想了解原因。谢谢,李。
更新
我不认为我的问题在于事件总线部分。我想我明白了。我在该模型中编写的java问题几乎更多是在一个线程(工作线程,上面的代码)中编写的,而理论上它可以在另一个线程(UI线程)中同时读取。在我看来,需要在模型上实现某种并发访问控制。同步块或并发ArrayList
。但该模型的最终消费者是我在我的应用程序中无法控制的代码,它位于某个地方ArrayAdapter
。我无法为其添加同步。使用模型初始化ArrayAdapter
后,ArrayAdapter
拥有它并将从UI线程访问它。我不确定非UI线程是否可以通过这种方式修改模型。
答案 0 :(得分:1)
你是对的 - 这个示例应用程序存在缺陷。我试图积极地减少代码,我走得太远了。
需要进行的两项更改是:
AsyncDemoFragment
应该使用模型ArrayList<String>
的副本,从当前模型初始化,而不是实际 模型
我应该在WordReadyEvent
中提供这个词,以允许AsyncDemoFragment
更新其模型的副本,以及解雇notifyDataSetChanged()
这将允许后台线程在后台线程上更新和维护模型主副本,让UI线程根据需要了解更改,并允许UI线程检索该模型的副本以供UI使用。
在其他情况下,拥有共享的同步模型并非不可能。在这种情况下,这是不切实际的,缺少kcoppock关于创建自定义适配器类的建议,这就像在这种情况下用别克拍苍蝇一样。
我将努力解决这个问题,以便进行下一次书籍更新。
顺便说一句,我问你是否读过这一章的原因仅仅是因为有些人仅仅根据检查回购提出问题,我想知道你是否阅读了样本附带的材料。我的评论本来可以解释为如果你阅读这一章你会得到你的问题的答案,我绝对不是那个意思。如果我糟糕的短语引起你的任何问题,我道歉。感谢你指出这个问题!