【问题标题】:How to iterate over a map while concurrently adding values to it?如何在向地图添加值的同时迭代地图?
【发布时间】:2016-09-19 10:40:32
【问题描述】:

我持有一个对象地图,每次将一个对象添加到其中时,都需要通知地图中的所有其他对象新对象,反之亦然。这些对象在它们自己的 thread 进程上运行(每个都从main 开始)可以调用将它们添加到映射的方法,因此可以在迭代期间添加对象对象。

这是我的一些示例代码。这是我在上面谈论的对象

class Notifier {

    String name;
    Hub hub;

    Notifier(String name) {

        this.name = name;
        hub.add(this);
    }

    void acknowledge(String name) {

        System.out.println(this.name + " was notified of " + name);
    }
}

这是拿着地图的东西

public class Hub {

    ConcurrentMap<String, Notifier> map = new ConcurrentHashMap<>();

    void add(Notifier notifier) {

        map.putIfAbsent(notifier.name, notifier);

        Iterator<Entry<String, Notifier>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, Notifier> entry = it.next();
            if (!entry.getKey().equals(notifier.name)) {
                entry.getValue().acknowledge(notifier.name);
                notifier.acknowledge(entry.getKey());
            }
        }
    }
}

我试过ConcurrentHashMap,但不一定是这样。我的问题是我得到的结果是,如果我一起启动它们的线程,每个对象都会多次收到关于另一个对象的通知。我明白了

notifier1 被通知了 notifier2
notifier1 被通知了 notifier2
notifier1 被通知了 notifier3
notifier1 被通知了 notifier3
notifier1 被通知了 notifier4
notifier1 被通知了 notifier4

其他的也一样。如果我一个接一个地启动它们,那么我让每个完成添加我得到正确的结果

notifier1 被通知了 notifier2
notifier1 被通知了 notifier3
notifier1 被通知了 notifier4

其他类似。

我知道我选择的这张地图不能保证在添加(放置)值时更新迭代器,所以我认为这就是它发生的原因。无论如何,我知道上述实验中存在一些线程竞争。

我怎样才能让每个对象都被准确地通知所有其他对象的 1 次?也许这个并发地图不好,我需要同步一些东西?我不在乎添加的顺序。

【问题讨论】:

  • 这些是什么类型的对象?是否有充分的理由让它们成为独立线程而不是使用回调方法?
  • @chrylis 它就像客户端和服务器。每个进来的客户都会收到所有其他客户的通知,他们也会收到通知。这不是一种回调,因为集线器调用了通知对象上的方法吗?对不起,如果我不明白。
  • 有点,但您特别说它们“在自己的线程上运行”,这与您在此处发布的代码不匹配。
  • @chrylis 每个都是通过main单独启动的,所以它们是不同的应用程序。我认为这也意味着它们在不同的线程上。
  • 哦,那么它们是完全独立的进程,这使得这是一个完全不同的问题。嗯。

标签: java concurrency synchronization iteration concurrenthashmap


【解决方案1】:

我认为错误很容易。如果通知者重复,您不会中止通知。

public class Hub {

    ConcurrentMap<String, Notifier> map = new ConcurrentHashMap<>();

    void add(Notifier notifier) {

        if (map.putIfAbsent(notifier.name, notifier) == null) {

            Iterator<Entry<String, Notifier>> it = map.entrySet().iterator();
            while (it.hasNext()) {
                Entry<String, Notifier> entry = it.next();
                if (!entry.getKey().equals(notifier.name)) {
                    entry.getValue().acknowledge(notifier.name);
                    notifier.acknowledge(entry.getKey());
                }
            }
        }
    }
}

【讨论】:

  • 但不会多次启动同一个通知程序 - 每个都只调用一次 add 方法。我会更新我的代码,看来我的水比我最初想象的要深。
  • 如果你有几个同名的,例如相同的键然后映射防止重复,但无论如何都会发送通知。
  • 我没有几个同名的。构造函数有一个唯一的名称。
  • 正如我之前所说,向我们展示更多代码。我试图猜测什么是错误的并且失败了,因为没有代码它相当棘手:)
猜你喜欢
  • 1970-01-01
  • 2020-10-31
  • 1970-01-01
  • 2011-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-30
  • 1970-01-01
相关资源
最近更新 更多