> I have noticed in Java 25 (and earlier versions) that calling
> `ThreadLocalRandom.current().nextGaussian()` uses the Random.nextGaussian()
> implementation, which is synchronized. Under heavy load in a multithreaded
> environment, I was detecting lock contention.
>
> Here is a very simple reproducer:
>
> ThreadLocalRandom r = ThreadLocalRandom.current();
> // step into this call using a debugger
> r.nextGaussian();
>
>
> It dispatches to the synchronized Random implementation, since
> ThreadLocalRandom extends Random, thus the default implementation (not
> synchronizing) on RandomGenerator is not used.
>
> Sketch:
>
> public interface RandomGenerator {
> default double nextGaussian() {
> // remove TAOCP comment since it is out of date, and this uses
> the ziggurat algorithm instead
> return RandomSupport.computeNextGaussian(this);
> }
> }
>
> public class Random implements RandomGenerator {
> @Override
> public synchronized double nextGaussian() {
> // synchronized version ...
> }
> }
>
> public class ThreadLocalRandom extends Random {
>
> // ADD THIS
> @Override
> public double nextGaussian() {
> return RandomSupport.computeNextGaussian(this);
> }
> }
>
>
> A comment on ThreadLocalRandom states "This implementation of
> ThreadLocalRandom overrides the definition of the nextGaussian() method in
> the class Random, and instead uses the ziggurat-based algorithm that is the
> default for the RandomGenerator interface.” However, there is none such
> override happening. It appears that prior to
> a0ec2cb289463969509fe508836e3faf789f46d8 the nextGaussian implementation was
> non-locking since it used proper ThreadLocals.
>
> I conducted an audit of all of the RandomGenerator and Random methods to see
> if there are any others:
>
>
> Set<String> tlrMethods = new HashSet<>();
> for (Method method :
> java.util.concurrent.ThreadLocalRandom.class.getDeclaredMethods()) {
> int mod = method.getModifiers();
> if (!Modifier.isStatic(mod) && Modifier.isPublic(mod)) {
> String desc =
> method.getReturnType() + " " + method.getName()
> + " " + Arrays.toString(method.getParameters());
> tlrMethods.add(desc);
> }
> }
> for (Method method : java.util.Random.class.getDeclaredMethods()) {
> int mod = method.getModifiers();
> if (!Modifier.isStatic(mod) && Modifier.isPublic(mod)) {
> String desc =
> method.getReturnType() + " " + method.getName()
> + " " + Arrays.toString(method.getParameters());
> if (!tlrMethods.contains(desc)) {
> System.out.println(desc);
> }
> }
> }
>
>
> That prints
>
> void nextBytes [byte[] arg0]
> double nextGaussian []
>
>
> The former safely calls `Thread...
James Yuzawa has updated the pull request incrementally with one additional
commit since the last revision:
8372134: ThreadLocalRandom no longer overrides nextGaussian
Fix javadoc
-------------
Changes:
- all: https://git.openjdk.org/jdk/pull/28483/files
- new: https://git.openjdk.org/jdk/pull/28483/files/d9350595..245dfb4d
Webrevs:
- full: https://webrevs.openjdk.org/?repo=jdk&pr=28483&range=01
- incr: https://webrevs.openjdk.org/?repo=jdk&pr=28483&range=00-01
Stats: 19 lines in 1 file changed: 9 ins; 9 del; 1 mod
Patch: https://git.openjdk.org/jdk/pull/28483.diff
Fetch: git fetch https://git.openjdk.org/jdk.git pull/28483/head:pull/28483
PR: https://git.openjdk.org/jdk/pull/28483