Reputation: 13372
class Nodes
has getNodes()
method, which is not synchronized
. But List<Node> nodes
- is synchronized
. Many threads could be connected to it, changing nodes
in it.
Like this:
class Nodes {
List<Node> nodes = Collections.synchronizedList(new ArrayList<Node>() );
public List<Nodes> getNodes() { return nodes; }
...
}
Client code:
Nodes nodes;
synchronized(nodes) {
for(Node node: nodes.getNodes()) {
...
}
}
I do not have interrogation tests for that, but:
Should I use while(iterator.hasNext()) { var = iterator.next() }
instead of for-loop ?
Because I know that when I try to delete nodes.remove(node)
inside for-loop it fails with ConcurentModificationException
.
EDIT: (related issue)
If iterator is good stuff to use, then having this code (client code):
Iterator<Node> iter = nodes.getNodes().iterator();
while (iter.hasNext()) { // line 1
Node node = iter.next(); // line 2
}
It is not safe anyway:
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.
Does it mean that I should do locking on client side anyway. This is what I don't want to do.
One of the solutions I have:
while (iter.hasNext()) {
try {
Node node = iter.next();
...
} catch (NoSuchElementException ex) {continue;} // handle exception - do more try
}
EDIT:
Answer for my case was: to use CopyOnWriteArrayList. I can even stay with for-loop
with it.
But another option: Just return client a copy of the list to let them know whatever they want with it. Because it is kind of strange (inconsistent) providing 'snapshot iterator' AND real data in the list at the same time.
Upvotes: 2
Views: 1737
Reputation: 13372
What is even better:
To use:
private List<Node> defensiveCopyNodeList() {
List<Node> nodesListCopy = Lists.newLinkedList();
synchronized (nodesList) {
nodesListCopy = ImmutableList.copyOf(nodesList); // Google [Guava lib][1]
}
return nodesListCopy;
}
Then in getter:
public List<Node> getNodes() {
return defensiveCopyNodeList();
}
Then it allows us to use safely not only iterator but and data itself.
Upvotes: 0
Reputation: 17705
You should use an iterator like you have suggest, but instead of doing a nodes.delete()
(which is really a nodes.remove(...)
) you should instead do iterator.remove()
You have updated your question. Here's an updated answer addressing the 'atomicity' of the iterator. If you want your iterator to have a 'snapshot' of the values at the time it (the iterator) was created, then you can use the Concurrent set of collections in java.util.concurrent: like CopyOnWriteArrayList
Upvotes: 2
Reputation: 2845
Iterator.remove is the only safe way to modify a collection during iteration
Source: The Collection Interface tutorial
Upvotes: 4