I have a class (lets call it XClass) which has a method (lets call it xMethod), that I am testing. It also contains a:
private static Map<String, String> map = new HashMap<String, String>();
In my setup method of the unit test I have:
ReflectionTestUtils.setField(xClass, "map", map, null);
In the test method I create couple of (in my case 8) threads. Their run method invokes xClass.xMethod. This method changes the static map variable. The method xMethod, is supposed to invoke map.containsKey() map.get() and map.put() 8 times. It doesn’t do any remove. This method also doesn’t create any new threads, so the map shouldn’t be changed once the thread is finished with the xMethod. I wait for all of the threads to finish (either normally or by an exception). Than I check the map
int mapSize = map.size();
assertEquals("map:" + map, 8, mapSize);
It fails here with the message:
java.lang.AssertionError: map:{3=x1, 2=x2, 1=x3, 7=x4, 6=x5, 5=x6,
4=x7, 8=x8} expected:<8> but was:<7>
I solved the problem by using a ConcurrentHashMap, but I am still puzzled with the issue. How is it possible after all 8 threads finish, for the map to behave weird (size() returns 7 but toString() prints 8 entities)? I would have understood if there were 7 entites and the size() method gave 7, but there are 8 entities in the map. How is this possible?!
Btw, I checked the termination of the threads in couple of ways:
1. with a check for the Thread.State.TERMINATED.
2. I printed a simple message before the method return, printed a simple message after the threads were done (and checked there were no exceptions thrown. The 8 messages are always written before the 9th (in the test after threads “finished”).
3. Even done a simple thread which in the run method contained the following logic:
public void run() {
try {
obj = xClass.xMethod();
} catch (Exception e) {
exc = e;
}
finished = true;
}
Than I would only loop indefinitely until all of the threads had their finished == true.
All of these imply that the threads are finished before I continue with asserting and checking the map. So how, again, is it possible for the map.size() to return 7 and the map.toString() to return 8 entities?!
Kind Regards,
despot
By concurrently mutating the map you have disrupted its internal invariants and you are observing unspecified and unpredictable behavior, which is the expected result.