2

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

因为我知道当我尝试nodes.remove(node)在 for 循环中删除时,它会以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和它在一起。

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

4

3 回答 3

4

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

来源:集合界面教程

于 2013-05-24T20:04:33.463 回答
2

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

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

于 2013-05-24T20:04:46.013 回答
0

更好的是:

要使用:

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

然后在吸气剂中:

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

然后它让我们不仅可以安全地使用迭代器,还可以安全地使用数据本身

于 2013-05-27T14:19:39.120 回答