A few weeks ago, I blogged about a caching approach for ColdFusion 9 in which the business logic for "cache key" creation was factored out of the main algorithm. In a spin-off conversation in the comments, Dennis Clark raised the issue of thread safety surrounding my use of an auto-incrementer:
id = ++this.autoIncrementer
He claimed that in extremely rare cases, this line of code could actually result in a race condition. While I am not 100% convinced that this is true, I was interested in the solution he suggested: use Java's AtomicInteger rather than a standard int value. I had never heard of an AtomicInteger before; but after looking it up, it appears that Java has a small set of "atomic" data types that are built to provide thread-safe, single-variable mutation without locking (NOTE: avoidance of locking is not guaranteed). Given the fact that these exist, and that the AtomicInteger is built for incrementing, I guess it's only logical to believe the ++ operator is not thread-safe.
Before I looked it up, however, I suggested that a named-lock could be put around the value increment; but what Dennis Clark told me (which the Java docs confirm) is that because the atomic data types don't use locking, they will be more performant. I figured this would be an easy thing to put to the test; in the following demo, I am going to compare the relative speed of incrementing between a ColdFusion named-lock and a Java AtomicInteger:
<!--- Create a standard integer. ---> <cfset counter = 0 /> <!--- Create an atomic integer. This specialized Number class allows us to increment the value without having to worry about thread-safey. NOTE: This class is designed for use with increment / decrement scenarios. ---> <cfset atomicCounter = createObject( "java", "java.util.concurrent.atomic.AtomicInteger" ) .init() /> <!--- Now, let's define a method for incrementing. It will work in two ways - both ways will be thread-safe; but, one way will use explicit named locking and one will rely on the the atomic integer class. ---> <cffunction name="incrementCounter" access="public" returntype="any" output="false" hint="I increment the given value with or without locking."> <!--- Define arguments. ---> <cfargument name="useLocking" type="boolean" required="true" hint="I determine if locking should be used." /> <!--- Check for locking. ---> <cfif arguments.useLocking> <!--- Use an explicit lock around our standard integer to make it safe. ---> <cflock name="counterIncrementLock" type="exclusive" timeout="2"> <!--- Increment and return standard value. ---> <cfreturn ++counter /> </cflock> <cfelse> <!--- Increment and return the atomic value. ---> <cfreturn atomicCounter.incrementAndGet() /> </cfif> </cffunction> <!--- Now, that we have are two ways of incrementing in place, let's run some light-weight performance tests. ---> <cfoutput> <!--- Start with the standard name-locking. ---> <cftimer type="outline" label="Named-Locking"> <cfloop index="i" from="1" to="10000" step="1"> <!--- Increment value with named lock. ---> <cfset incrementCounter( true ) /> </cfloop> Done (== #counter#) </cftimer> <!--- ------------------------------------------------- ---> <!--- ------------------------------------------------- ---> <!--- End with the atomic integer auto-safety. ---> <cftimer type="outline" label="Atomic-Integer"> <cfloop index="i" from="1" to="10000" step="1"> <!--- Increment atomic integer value. ---> <cfset incrementCounter( false ) /> </cfloop> Done (== #atomicCounter#) </cftimer> </cfoutput>
As you can see in the above code, each test attempts 10,000 value increments; once on the standard int value using the ++ operator and ColdFusion's CFLock tag and, once on Java's AtomicInteger using the incrementAndGet() method. Despite the fact that the AtomicInteger has some special methods, notice that it extends the Number class and can be treated like a standard number if necessary (such as when I am outputting it at the end with hash signs).
I ran the above page a number of times and quick saw this trend emerging:
Clearly, both of these methods used over 10,000 iterations are extremely fast! However, the AtomicInteger is about 100% faster than the use of named-locks. And, not only is it faster, but I find the incrementAndGet() method to be much more convenient than using the CFLock tag - especially when I only want to lock access to this one variable. The only trick is going to be remembering that you're dealing with a special kind of number.
This is very interesting stuff! Thanks to Dennis for directing me to a new and potentially useful Java class. I get the feeling that there is so much untapped Java goodness below the ColdFusion surface.
Want to use code from this post? Check out the license.
Related question I had on Stackoverflow: http://stackoverflow.com/questions/1811406/thread-safe-sequential-number-generator-in-coldfusion
I'm now using AtomicInteger, glad you tested it out for me that it works well. :)
Yeah, it seems cool. I am not sure how many times I will need to increment a value (in lieu of a database); but when I do, this will be a perfect solution.
WOW! @Ben, I'm honored that you thought my comments worthy enough to inspire your own investigation of AtomicInteger and its performance.
You, in turn, have inspired me to take your experiment just a little bit further. You can read about what I did and the discoveries I made back on my own blog:
Thanks again Ben!
It's all about good conversations and learning from each other :)
I just stumbled across the non-thread-safety of the ++ operator today! There's a repro case on my blog: