【问题标题】:Using iterator over for-loop in concurrent java app在并发Java应用程序中使用迭代器而不是for循环
【发布时间】:2013-05-24 20:01:09
【问题描述】:

class NodesgetNodes() 方法,即not synchronized。但是List<Node> nodes - is synchronized。许多线程可以连接到它,在其中更改nodes

像这样:

class Nodes {
 List<Node> nodes = Collections.synchronizedList(new ArrayList<Node>() );

 public List<Nodes> getNodes() { return nodes; }
 ...
}

客户端代码:

Nodes nodes;

synchronized(nodes) {

 for(Node node: nodes.getNodes()) {
  ...
 }

}

对此我没有审讯测试,但是:

我应该使用while(iterator.hasNext()) { var = iterator.next() }而不是for循环吗?

因为我知道当我尝试在 for 循环中删除 nodes.remove(node) 时,它会以 ConcurentModificationException 失败。


编辑:(相关问题)

如果迭代器是好东西,那么有这个代码(客户端代码):

Iterator<Node> iter = nodes.getNodes().iterator();
while (iter.hasNext()) {  // line 1
    Node node = iter.next();  // line 2
}

反正也不安全:

 1. thread1 goes to line 1, hoping that now iter would return him next() value. 
 2. but at that moment thread2 delete that value.
 3. thread1 has Exception and fails.

这是否意味着无论如何我都应该在客户端进行锁定。这是我不想做的。

我有一个解决方案:

while (iter.hasNext()) {
            
    try {
       Node node = iter.next();
       ...
                  
    } catch (NoSuchElementException ex) {continue;}  // handle exception - do more try
}

编辑:

我的答案是:使用 CopyOnWriteArrayList。我什至可以和 for-loop 在一起。

但是另一种选择:只需向客户返回列表的副本,让他们知道他们想要什么。因为在列表中同时提供“快照迭代器”和真实数据有点奇怪(不一致)。

【问题讨论】:

  • 总是推荐在多线程环境下使用Iterator..
  • @Ankur 你能澄清你的评论吗?增强的 for 确实在幕后使用了迭代器。
  • 另请注意,ConcurrentModificationException 与多线程无关。
  • “总是建议在多线程环境下使用Iterator”这条评论的依据是什么?

标签: java concurrency


【解决方案1】:

Iterator.remove 是唯一在迭代期间修改集合的安全方法

来源:The Collection Interface tutorial

【讨论】:

    【解决方案2】:

    您应该按照您的建议使用迭代器,但不要使用 nodes.delete()(实际上是 nodes.remove(...)),而应该使用 iterator.remove()

    您已更新您的问题。这是解决迭代器“原子性”的更新答案。如果您希望您的迭代器在创建它(迭代器)时具有值的“快照”,那么您可以使用 java.util.concurrent 中的 Concurrent 集合集:如 CopyOnWriteArrayList

    【讨论】:

    • 我已经解决了这个问题。谢谢。我添加了更多编辑。
    • 这很有趣。你认为它比在 while 循环中捕获 NoSuchElementException 好得多吗?这样看来,每次阅读都不需要复制,也不需要使用新的结构,这让人们感到困惑。
    • 可能不会。我觉得那个异常处理不是线程安全的。这意味着我可以等待/重复一些不真实的事情。
    • 但是另一种选择:只需将列表的副本返回给客户,让他们知道他们想要什么。因为在列表中同时提供“快照迭代器”和真实数据有点奇怪(不一致)。
    【解决方案3】:

    还有什么更好的:

    使用方法:

    private List<Node> defensiveCopyNodeList() {
        List<Node> nodesListCopy = Lists.newLinkedList();
        synchronized (nodesList) {
            nodesListCopy = ImmutableList.copyOf(nodesList);  // Google [Guava lib][1]
        }
        return  nodesListCopy;
    }
    

    然后在getter中:

    public List<Node> getNodes() {
          return  defensiveCopyNodeList();
    }
    

    然后它允许我们安全地使用不仅是迭代器,而且是数据本身

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-27
      • 2018-07-05
      • 2014-04-27
      • 1970-01-01
      • 2021-02-24
      • 2021-06-22
      • 1970-01-01
      相关资源
      最近更新 更多