为什么Observable快照观察者向量

时间:2010-12-15 04:33:54

标签: java

在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);
}

4 个答案:

答案 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参数。

我可以看到避免这种情况的唯一解决方案是将调用同步到setChangednotifyObservers,以便它们不会交错。这当然意味着观察者在同步块内得到通知,这违反了作者声明不应该发生的声明。因此,很难推荐使用这个类。