Reading the thread profile data: synchronization issues
So far in this discussion, we've seen how to create a thread that sits
the JVM for the current stack traces (via a ThreadMXBean) of each thread.
From this information, we can build up a map of [code position]->[count] information.
What we haven't done so far is actually read the profiling information
we've so painstakingly gathered!
We need some form of data synchronization
to ensure that we can safely read
a current snapshot of the profiling data at a given moment, and similarly that
the profiler thread can correctly update the data without fear of exposing
any reading thread to corrupt data. There are a few options available:
- we could hold the data in a ConcurrentHashMap;
- we could use plain old synchronized around access to the map;
- we could use one of the more explicit locking mechanisms provided by Java 5.
Using a ConcurrentHashMap
could work quite well for us, and would
certainly allow the profiler thread to write to the data while a reader thread
was iterating over the map.
But it's arguably ever so slight overkill: ConcurrentHashMap
is essentially designed to improve concurrency when we regularly have multiple concurrent
accesses. In our case, accesses to read the data by the reader thread will actually
be very rare in comparison to the scores or even hundreds of writes to the map
being performed every second by the actual profiler thread.
Plain old synchronized
actually isn't such a bad choice in this case.
In modern JVMs, synchronized implementations are optimised for uncontended access,
and possibly for repeated access by the same thread: in other words,
precisely the situation we have with our profiler thread.
In this particular case, I'm actually going to opt for using a more explicit locking
mechanism. I'm going to use an AtomicInteger to handle the lock.
The only real rationale for doing so is that in this case, we know something about
the other thread that could possibly be accessing the data, and so by doing things at
a low level, we can guide the VM on what to do when there's contention. With
synchronized or even something like ReentrantLock, we rely on
the VM's generic implementation deciding to "do the right thing".
On the next page, we'll see how we use an AtomicInteger as a lock to synchronize our data.