在并发Java应用程序中使用迭代器而不是for循环

时间:2013-05-24 20:01:10

标签: java concurrency

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-loop吗?

因为我知道当我尝试在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呆在一起。

但另一种选择:只需返回客户端列​​表的副本,让他们随心所欲地了解它。因为同时在列表中提供“快照迭代器”和真实数据是一种奇怪的(不一致的)。

3 个答案:

答案 0 :(得分:4)

  

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

来源:The Collection Interface tutorial

答案 1 :(得分:2)

你应该像你建议的那样使用迭代器,但不应该使用nodes.delete()(实际上是nodes.remove(...)),而应该iterator.remove()

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

答案 2 :(得分:0)

更好的是:

使用:

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

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