Lets say I’m interacting with a system that has two incrementing counters which depend on each other (these counters will never decrement):
int totalFoos; // barredFoos plus nonBarredFoos
int barredFoos;
I also have two methods:
int getTotalFoos(); // Basically a network call to localhost
int getBarredFoos(); // Basically a network call to localhost
These two counters are kept and incremented by code that I don’t have access to. Let’s assume that it increments both counters on an alternate thread but in a thread-safe manner (i.e. at any given point in time the two counters will be in sync).
What is the best way to get an accurate count of both barredFoos and nonBarredFoos at a single point in time?
The completely naive implementation:
int totalFoos = getTotalFoos();
int barredFoos = getBarredFoos();
int nonBarredFoos = totalFoos - barredFoos;
This has the issue that the system could increment both counters in between the two method calls and then my two copies would be out of sync and barredFoos would have a value of more than it did when totalFoos was fetched.
Basic double-checked implementation:
while (true) {
int totalFoos = getTotalFoos();
int barredFoos = getBarredFoos();
if (totalFoos == getTotalFoos()) {
// totalFoos did not change during fetch of barredFoos, so barredFoos should be accurate.
int nonBarredFoos = totalFoos - barredFoos;
break;
}
// totalFoos changed during fetch of barredFoos, try again
}
This should work in theory, but I’m not sure that the JVM guarantees that this is what actually happens in practice once optimization and such is taken into account. For an example of these concerns, see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html (Link via Romain Muller).
Given the methods I have and the assumption above that the counters are in fact updated together, is there a way I can guarantee that my copies of the two counts are in sync?
Yes, I believe your implementation will be sufficient; the real work is making sure that the values that are returned by
getTotalFoosandgetBarredFoosare indeed synchronized and always returning the latest values. However, as you’ve said, this is already the case.Of course, one thing you could run in to with this code is an endless loop; you would want to be sure that the two values being changed in such a short time would be a very exceptional situation, and even then I think that it would definitely be wise to build in a safety (ie maximum number of iterations) to avoid getting into an endless loop. If the value coming out of those counter is in code that you don’t have access to, you don’t want to be totally relying on the fact that things will never go awry at the other end.