java - Listener/Observable 实现,同步与并发集合

标签 java concurrency

在实现线程安全的监听器时,我通常想知道哪种类型的 Collection 最适合容纳监听器。到目前为止,我找到了三个选项。

标准的Observable 使用同步 访问一个简单的ArrayList。使用监听器的副本是可选的,但据我所知,这是一个好主意,因为它可以防止像

这样的问题
  • 监听器在回调中删除自身(ConcurrentModificationException - 可以通过索引 for 循环以相反的顺序迭代以防止发生这种情况)
  • 在同步块(synchronized block)内执行外部代码可以阻止整个事情。

不幸的是,实现不止几行。 Collections.synchronizedList() 不需要 removeListener 中的 synchronized 但这并不值得。

class ObservableList {
    private final List<Listener> listeners = new ArrayList<Listener>();
    public void addListener(Listener listener) {
        synchronized (listeners) {
            if (!listeners.contains(listener)) {
                listeners.add(listener);
            }
        }
    }
    public void removeListener(Listener listener) {
        synchronized (listeners) {
            listeners.remove(listener);
        }
    }
    protected void notifyChange() {
        Listener[] copyOfListeners;
        synchronized (listeners) {
            copyOfListeners = listeners.toArray(new Listener[listeners.size()]);
        }
        // notify w/o synchronization
        for (Listener listener : copyOfListeners) {
            listener.onChange();
        }
    }
}

但是 java.util.concurrent 中的 Collection 本质上是线程安全的并且可能更高效,因为我认为它们的内部锁定机制得到了更好的优化而不是简单的 synchronized block 。为每个通知创建一个副本也非常昂贵。

基于CopyOnWriteArrayList

class ObservableCopyOnWrite {
    private final CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>();
    public void addListener(Listener listener) {
        listeners.addIfAbsent(listener);
    }

    public void removeListener(Listener listener) {
        listeners.remove(listener);
    }

    protected void notifyChange() {
        for (Listener listener : listeners) {
            listener.onChange();
        }
    }
}

应该与第一个版本大致相同,但副本较少。添加/删除监听器不是一个非常频繁的操作,这意味着副本也不应该非常频繁。

我通常使用的版本是基于 ConcurrentHashMap 的,其中声明了 .keySet() 在这里用作 Set:

The view's iterator is a "weakly consistent" iterator that will never throw ConcurrentModificationException, and guarantees to traverse elements as they existed upon construction of the iterator, and may (but is not guaranteed to) reflect any modifications subsequent to construction.

这意味着迭代至少包括在开始迭代时注册的每个监听器,甚至可能包括在迭代期间添加的新监听器。不确定已删除的听众。我很喜欢这个版本,因为它没有复制,而且与 CopyOnWriteArrayList 一样易于实现。

class ObservableConcurrentSet {
    private final Set<Listener> listeners = Collections.newSetFromMap(new ConcurrentHashMap<Listener, Boolean>());

    public void addListener(Listener listener) {
        listeners.add(listener);
    }

    public void removeListener(Listener listener) {
        listeners.remove(listener);
    }

    protected void notifyChange() {
        for (Listener listener : listeners) {
            listener.onChange();
        }
    }
}

基于 ConcurrentHashMap 的实现是一个好主意还是我在这里忽略了什么?或者是否有更好的 Collection 可供使用?

最佳答案

Is a ConcurrentHashMap based implementation a good idea or did I overlook something here? Or is there an even better Collection to use?

ConcurrentHashMap 在这种情况下似乎没问题。它肯定比使用 Collections.synchronizedList() 更好。如果在迭代器遍历 map 时添加新条目,CHM 迭代器可能会也可能不会看到 map 中的新条目。如果它们在迭代期间被删除,它也可能会或可能不会看到旧条目。这两者都是由于关于何时添加/删除有问题的节点以及迭代器所在位置的竞争条件。但是迭代器总是会看到 map 中的项目,只要它们没有被删除。

关于java - Listener/Observable 实现,同步与并发集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18964661/

相关文章:

java - HashMap<String, boolean> 将所有键复制到 HashMap<String, Integer> 并将值初始化为零

java - 使用 Java 或 Spring 生成 PDF

java - log4j 需要 JSF jar 吗?

java - JSF 2.0 和 TransformerFactory

java - volatile 未按预期工作

java - 此生产者消费者的非法监视器状态异常?

java - 在 java : automatic thread-safety? 中序列化

java - Jpa递归查询

c++ - 用于同步的条件变量与原子类型

Java 线程优先级