Win a copy of Cross-Platform Desktop Applications: Using Node, Electron, and NW.js this week in the JavaScript forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

Did the For loop lie  RSS feed

 
junchen liu
Greenhorn
Posts: 26
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
There are few thing we all know:

1> Vector is synchronized (or thread safe) container
2> The Iterator returned from Vector is fail-fast(if the Vector is structurally modified at any time after the Iterator is created, in any way except through the Iterator's own remove or add methods, the Iterator will throw a ConcurrentModificationException)

thus I created a test program:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package z_project.concurrent;

import java.util.Iterator;
import java.util.Vector;


public class Main {

public static void main(String[] args) throws InterruptedException {

//add item into the Vector every 2 seconds
class AddItemThread extends Thread {

private Vector<Long> v;

public AddItemThread(Vector<Long> v) {
this.v = v;
}

public void run() {
while (true) {
try {
Thread.sleep(1000 * 2);
} catch (InterruptedException ex) {
}

v.add(System.currentTimeMillis());
}
}
}

//keep loop through the Vector
class LoopVectorThread extends Thread {

private Vector<Long> v;

public LoopVectorThread(Vector<Long> v) {

this.v = v;

}

@Override
public void run() {
while (true) {
Iterator i = v.iterator();
while (i.hasNext()) {
System.out.println(i.next());
}
}
}
}

//create the vector object and start the thread
Vector<Long> vector = new Vector();
AddItemThread addThread = new AddItemThread(vector);
LoopVectorThread loopThread = new LoopVectorThread(vector);
addThread.start();
loopThread.start();
}
}


then I have got the ConcurrentModificationException like this:

Exception in thread "loop-thread" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at z_project.concurrent.Main$1LoopVectorThread.run(Main.java:54)

however this can be solved by:

//block the addThread while loop through the Vector
synchronized (v) {
Iterator i = v.iterator();
while (i.hasNext()) {
System.out.println(i.next());
}
}

I want find another solution instead of "synchronized" the Vector
so I did:
for (Long item : v) {
System.out.println(item);
}

there wasn't a exception........ everything goes fine


my question is:

1> does the for loop actually thread safe?
2> does the for loop works more efficient, because it never
blocks other thread from modify the vector
 
Steve Luke
Bartender
Posts: 4181
22
IntelliJ IDE Java Python
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
If there wasn't an exception when using the for loop than it was a coincidence of timing. When I ran the tests it took 3 tries to get an error on the iterator and 5 tries to get the same error using the for loop.

The for loop uses an iterator behind the scenes. It is not synchronized. If you want to prevent the concurrent modification exceptions you need to use a synchronized block.
 
Nitesh Kant
Bartender
Posts: 1638
IntelliJ IDE Java MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Please UseCodeTags while posting.
It will be great if you can take a moment to edit your message and add code tags.
 
junchen liu
Greenhorn
Posts: 26
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
1> If there wasn't an exception when using the for loop than it was a coincidence of timing.
2> When I ran the tests it took 3 tries to get an error on the iterator and 5 tries to get the same error using the for loop.
3>The for loop uses an iterator behind the scenes. It is not synchronized. If you want to prevent the concurrent modification exceptions you need to use a synchronized block.


Thanks for the replies guys, there are my thoughts

1> if there wasn't an exception then it WAS a coincidence of timing? I think its not right. because from my example while one thread loop through the vector another thread keep pushing item on the vector, obviously the vector was modified while you loop through it. and the exception was never thrown!!! (the test has been running for a day now)

2>which test did you run? certainly not mine right? because the test I have provided, using iterator loop through the vector in a non-stop fashion, it
must throw exception every single time.

3> how did you know that for loop uses an iterator behind the scenes?

Many thanks
 
Steve Luke
Bartender
Posts: 4181
22
IntelliJ IDE Java Python
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
1> Yes, if there was NOT an Exception than it WAS a coincidence of timing. Like I said, when I do it in my code, I did get an exception. I was able to force the exception to occur by making the loop take longer to finish a single iteration (add a short sleep to it).

2> I ran something very similar to yours but I kept stopping/starting it if the error didn't happen in the first 10 - 20 loops. So when I was running the iterator I had stopped 3 times, then the exception occurred almost right away. With the for loop I stopped/restarted 5 times before the error occurred. When I added a sleep(100) in the middle of the iteration both failed very quickly every time.

3> How do I know? Because Sun told me so.. "... you don't have to declare the iterator, ... (The compiler does this for you behind your back...)". I could probably come up and check the binary to be sure, but at the moment I trust Sun and take their word for it.

So the point is that you should not assume the enhanced for loop is synchronized. Absolutely nowhere does it say it is. Careful testing shows it is not. If it appears to be in your hands then you need to take it as an oddity and context-specific, not the standard.
 
Steve Luke
Bartender
Posts: 4181
22
IntelliJ IDE Java Python
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Here is the code that I used. There is one class, four methods for iterating over the vector (Iterator, For:Each, Iterator with sleep, For:Each with sleep). In the startWorking method I create the runnables used to do the filling/iterating. Change the method called from the second runnable to test the different iteration methods. When the sleep is introduced to both iterations they both fail very quickly (3-5 iterations). When no sleep is introduced the Iterator seems to fail quicker (or more often) then the for loop.


[ November 05, 2008: Message edited by: Steve Luke ]
 
junchen liu
Greenhorn
Posts: 26
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
hi Luke

3> How do I know? Because Sun told me so.. "... you don't have to declare the iterator, ... (The compiler does this for you behind your back...)". I could probably come up and check the binary to be sure, but at the moment I trust Sun and take their word for it.


the link which you gave me, doesn't say anything about "for loop uses an iterator behind the scenes". does it?
 
Steve Luke
Bartender
Posts: 4181
22
IntelliJ IDE Java Python
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Okay, went back and checked the compiled code using javap. Here are the important parts from the showValuesWithIterator and showValuesWithFor methods:


So using an iterator you see where the Vector.iterator(), Iterator.next(), casting from Object -> Long -> long, and Iterator.hasNext() methods get called.

In the for loop:


You see the same sequence of method calls using the for-each construct. So for-each uses an Iterator. What doesn't exist in either case is any sign of synchronization - which would provide 'monitorenter' and 'monitorexit' commands.
[ November 05, 2008: Message edited by: Steve Luke ]
 
Steve Luke
Bartender
Posts: 4181
22
IntelliJ IDE Java Python
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by junchen liu:
hi Luke



the link which you gave me, doesn't say anything about "for loop uses an iterator behind the scenes". does it?


It does, in the part I quoted. 'You don't have to declare the iterator, the compiler does that behind your back.'

Hmm, re-reading the article as a skeptic it seems like the quoted text is more talking about the generic declaration than the iterator (though to add a generic declaration to an iterator the iterator must first be created... but that is a leap). There may be a better reference that explicitly says the for-each loop uses an iterator. But the compiled code I showed above should be conclusive enough.

[ November 05, 2008: Message edited by: Steve Luke ]

The language specification to the rescue:
JLS - Blocks - enhanced for(�14.14.2).


[ November 05, 2008: Message edited by: Steve Luke ]
 
junchen liu
Greenhorn
Posts: 26
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
yes Luke, I admit it was my mistake, there is iterator behind the enhanced for loop, therefor it's definitely not thread safe.

however, this raised another question, "why iterator throws concurrentModificationException at the firs place ayway" I mean most of the
time when I loop over the collection, I don't mind other thread inserting or deleting element from the collection, as long as I am able to keep doing my job.

well, I can see that cocurrentModificationEsxception is more like a notification, it tells me "something has changed", however why should I care
, If I don`t ,can I ignore the notification?
 
Steve Luke
Bartender
Posts: 4181
22
IntelliJ IDE Java Python
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The problem with modifying a list when iterating over it is order. Iterator starts at the front then moves to the back hitting each object in the list along the way. If it is trying to access the 4th object, and an item was inserted at position 3 after you already touched position 3, then what gets returned as item 4? What used to be position 3? What used to be position 4? The problem is that you can't really tell, and what might happen would be that the link the iterator follows to the next item would be in an inconsistent state and you will get a null pointer, or the wrong results. At very least you either missed an object along the way or will hit the same object twice, both of which aren't good. Add deleting to the mix and you get more complications. To avoid any of these complications it is best just fail-fast with the exception instead of risking un-known and un-traceable errors later on.
 
junchen liu
Greenhorn
Posts: 26
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks Luke, it was a decent Java lesson.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!