Some background: I created a contrived example to demonstrate use of VisualVM to my team. In particular, one method had an unnecessary synchronized
The same problem as here.
You're actually measuring the contention inside a PRNG with a shared state.
JDKRandomGenerator
is based on java.util.Random
which has seed
shared among all your worker threads. The threads compete to update seed
in the compare-and-set loop.
Why lock
improves performance then? In fact, it helps to reduce contention inside java.util.Random
by serializing the work: while one thread performs matrix multiplication, the other is filling the matrix with random numbers. Without lock
threads do the same work concurrently.
There is a lot to remember when using random number generators. Long story short, your quirks were caused because the generators have to collect enough entropy before they can give you a random number. By sharing the generator, each call requires entropy to 'fill back up', so it was your blocking point. Now, some generators work differently than others as to how they collect entropy, so some are more effected or chain, rather than build up from scratch. When you make the generators within the instance, each instance builds entropy on its own, so it's faster.
Let me point you to SecureRandom, in particular the class JavaDoc where it says, "Note: Depending on the implementation, the generateSeed and nextBytes methods may block as entropy is being gathered, for example, if they need to read from /dev/random on various unix-like operating systems." This is what you were seeing and why things were slow. Using a single generator, it kept blocking. Yes, it's thread-safe, but it's blocking while getting entropy (note that you were having contention within your threads as they waiting for the blocking methods to return from generating random numbers building entropy, etc). When you put in your own locks, you were giving it time to collect entropy and do its thing in a 'polite' manner. It may be thread safe, but that doesn't mean that it's nice or efficient when bombarded ;-)
Also, for anything using java.util.Random, from Random,
Instances of java.util.Random are threadsafe. However, the concurrent use of the same java.util.Random instance across threads may encounter contention and consequent poor performance. Consider instead using ThreadLocalRandom in multithreaded designs.