在Observable的notifyObservers方法中,为什么编码器使用arrLocal = obs.toArray();
?
为什么编码器不直接迭代向量?谢谢
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
/* We don't want the Observer doing callbacks into
* arbitrary code while holding its own Monitor.
* The code where we extract each Observable from
* the Vector and store the state of the Observer
* needs synchronization, but notifying observers
* does not (should not). The worst result of any
* potential race-condition here is that:
* 1) a newly-added Observer will miss a
* notification in progress
* 2) a recently unregistered Observer will be
* wrongly notified when it doesn't care
*/
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
答案 0 :(得分:2)
他们希望避免并发修改,但同时不要长时间停留在同步块中(特别是当他们不知道他们实际调用的代码时)。
选项一是同步整个操作,并在通知观察者时直接迭代向量。正如评论指出的那样(“我们不希望观察者在保持自己的监视器的同时对任意代码进行回调”),这将使Observable可能长时间处于锁定状态。
选项二是只进行足够长的同步以获得向量的一致副本。 然后他们可以在迭代私人副本之前释放锁。
更新:如果Observer更新了Observers列表,那么同时迭代它可能不是一个好主意。因此,即使在单线程场景中也建议使用副本。
答案 1 :(得分:1)
此主题中的其他答案对于副本的目的是正确的。不过,我会说Java 5+已经拥有了自动复制的正确数据结构:java.util.concurrent.CopyOnWriteArrayList
。
答案 2 :(得分:0)
编码器使用“obs.toArray()”来“快照”当前的观察者。他们试图阻止迭代可能在其下面改变的向量,而不是在Vector周围明确同步。
答案 3 :(得分:0)
该代码实现了notifyObservers
应该通知那些在通话时(或至少在复制时)注册的观察者的语义。
如果不需要这种语义,那么只要迭代是线程安全的,那么作者可以简单地迭代调用更新的观察者,正如你所建议的那样。事实上,这就是我写它的方式。
关于如何编写这个类,它对我来说是坏的。看起来,通知人被要求编写如下代码:
observable.setChanged();
observable.notifyObservers(obj1);
也就是说,通知程序首先将observable标记为已更改,否则notifyObservers
不执行任何操作。另请注意,调用notifyObservers
会调用clearChanged
作为其操作的一部分。
因此,请考虑两个线程正在进行通知的情况。这两个命令可以跨线程交错,如下所示:
Thread 1: observable.setChanged();
Thread 2: observable.setChanged();
Thread 1: observable.notifyObservers(obj1);
Thread 2: observable.notifyObservers(obj2);
在这种情况下,线程1对notifyObservers
的第一次调用按预期工作。但线程2对notifyObservers
的第二次调用没有任何作用,因为第一次调用清除了更改的标志。所以观察者永远不会看到obj2
参数。
我可以看到避免这种情况的唯一解决方案是将调用同步到setChanged
和notifyObservers
,以便它们不会交错。这当然意味着观察者在同步块内得到通知,这违反了作者声明不应该发生的声明。因此,很难推荐使用这个类。