RFR: 8372134: ThreadLocalRandom no longer overrides nextGaussian

James Yuzawa duke at openjdk.org
Tue Nov 25 00:19:54 UTC 2025


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 `ThreadLocalRandom.nextInt()` internally. And the latter is fixed in this PR.

-------------

Commit messages:
 - 8372134: ThreadLocalRandom no longer overrides nextGaussian

Changes: https://git.openjdk.org/jdk/pull/28483/files
  Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=28483&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8372134
  Stats: 9 lines in 2 files changed: 8 ins; 1 del; 0 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


More information about the core-libs-dev mailing list