如何在同时向其添加值的同时迭代地图?

时间:2016-09-19 10:40:33

标签: java concurrency synchronization iteration concurrenthashmap

我拿着一个物体地图,每次添加一个物体时,地图中的所有其他物体都需要被通知新物体,反之亦然。这些对象在他们自己的线程进程上运行(每个对象都是从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 个答案:

答案 0 :(得分:0)

我认为错误非常简单。如果出现重复通知,则不会中止通知。

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