|
|
|
Posted by Aashish Patil (patil_aashish AT emc.com) on June 15th, 2007
|
A while back I had attended this ACM user meeting where Joshua Bloch and Neal Gafter (then at Sun and now at Google) presented a talk on Java puzzlers. They would display pieces of code that contained corner cases or subtle bugs. Attendees would then have to decipher the output of that code. It was an entertaining session and I left that session wanting more. I was rewarded a while later when they released their book called Java Puzzlers - Traps, Pitfalls and Corner Cases. I thoroughly enjoyed reading the book, which was an entertaining as well as a learning experience.
Its been some time since I read that book. When working on a recent project I had a bug of my own where I spent a considerable amount of time (and hair) trying to get the program working. The bug seemed a lot like one of the puzzles presented in the book and I thought of presenting it you. Here is the puzzle then.
The Puzzle
The following is a java.util.TimerTask that goes through a shared java.util.Map and prints all the keys in it. Assume that the Map contains the following keys inserted in that order - "key1", "key2", "key3" and "key4". Assume that the Map is an instance of java.util.HashMap. What is the output?
class MyTimerTask extends java.util.TimerTask
{
public void run()
{
Set keySet = myMap.keySet();
while (keySet.iterator().hasNext())
{
String key = (String) keySet.iterator().next();
System.out.println("key: " + key);
}
}
}
|
Well if you thought that it depends on whether there are multiple threads accessing the Map and that the output would depend on which thread gets scheduled and for how long - you are partially right. However, now assume that there is only one thread at a time accessing the Map. Thus, there is no complexity of simultaneous access by multiple threads. Given this, what is the output?
Solution
Since we are using a HashMap, the order in which the keys are inserted does not affect the order in which they are stored. A key will get inserted based on its hashcode. This is still not a pitfall. The above piece of code actually goes into an infinite loop. Due to the infinite loop it started to bring my machine to a crawl by locking up the CPU.
My first thought was that it might be because of the timer scheduling frequency. Was the frequency too high? To debug, I gave a scheduling interval of a high value such as a minute.
Even that did not help. The problem was something else. Looking at the output values, I realized that the same key was getting printed out in each loop. That meant that the iterator was not progressing. Was it because of some kind of hard-to-debug thread deadlock? I quickly dismissed this thought because the way I had setup the timer, only one thread would access the Map at any given time. I decided to print out each value that the iterator was returning. Upon doing this it looked like the iterator was always getting the same value. Eventually, I decided to printout to almost all statements in the program. It was then that I realized that the method Set#iterator() returns a different instance for every call.
Notice the two statements -
>>while (keySet.iterator().hasNext())
|
and
>>String key = (String) keySet.iterator().next();
|
When the first statement is invoked the set returns one iterator instance and when the second is invoked, another one. Thus, for each loop of the while statement, a different interator instance is getting returned. Due to this an iterator never gets exhausted and Iterator#hasNext() always returns true, resulting in an infinite loop. The right way to do this is to get a single instance of the iterator before the while loop and use that within the loop (guilty lines have been commented out) -
class MyTimerTask extends java.util.TimerTask
{
public void run()
{
Set keySet = myMap.keySet();
Iterator keyIter = keySet.iterator();
//while (keySet.iterator().hasNext())
while(keyIter.hasNext())
{
//String key = (String) keySet.iterator().next();
String key = (String) keyIter.next();
System.out.println("key: " + key);
}
}
}
|
Debugging
On a sidenote you might have noticed that I've used console prints as opposed to an IDE debugger. It is not that I don't use the IDE debugger and breakpoints. With printouts, I find that I can control exactly which statement I want more information about instead of having to step through a lot more statements. I can also control how much and exactly what information I want displayed with printouts.
Conclusion
If you have had similar experiences in your programming life from which we all can learn, feel free to share them on the EDN forums. The experiences could be even related to development on EMC.
Signing off till the next article...
About the Author
Aashish works in the EMC Developer Network(EDN) team and has prepared various pieces of content for the Component Exchange and other sections of the site. He is also the author of the Repository Interrogation Utility and the WDK Eclipse plugin distributed on the EDN.
|