Note that your examples have incorrect usage of ThreadLocalRandomSource: > > > > private IntegralValuedMutation(RandomSource rng) { > > provider = ThreadLocalRandomSource.current(rng); > > }
The provider returned from ThreadLocalRandomSource.current(...) should only be used within a single method. It should never be cached. This ensures thread safety. AFAIK the JVM is free to use different threads for code execution but all code within a single method must be on the same thread. So if you cache the UniformRandomProvider then it could end up being executed on a different thread. If you pass the cached value to multiple threads then they will all execute with the same RNG and it will break. The generator returned from ThreadLocalRandomSource.current should only be used locally. For example you can call this static method from concurrent threads: public static int nextInt(RandomSource rs) { return ThreadLocalRandomSource.current(rs).nextInt(); } A unique random generator will be created and seeded for the current thread if it has not yet been initialized. Otherwise the previously initialized generator is returned. This you cannot call from multiple concurrent threads (with the same rng): public static int nextInt(UniformRandomProvider rng) { return rng.nextInt(); } Do not cache the UniformRandomProvider from ThreadLocalRandomSource. You can cache the RandomSource enum value and then ensure all your implementations use the enum to obtain the UniformRandomProvider when it is required. The RandomSource enum is a factory for creating instances from the Commons RNG library. If you wish to allow a user to provide their own source of randomness, and it must be thread safe then you should specify a factory interface: public interface UniformRandomProviderFactory { UniformRandomProvider create(); } Then develop your code such that you ensure that any UniformRandomProvider requested from the library is only ever used on a single thread concurrently. For example: @Test void test() throws InterruptedException { // Wrong // UniformRandomProviderFactory factory = () -> ThreadLocalRandomSource.current(RandomSource.KISS); // Correct UniformRandomProviderFactory factory = () -> RandomSource.KISS.create(); AtomicLong total = new AtomicLong(); ExecutorService es = Executors.newCachedThreadPool(); long start = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { // Instance will only be used on one thread UniformRandomProvider rng = factory.create(); es.execute(() -> { long sum = 0; for (int j = 0; j < 10000000; j++) { sum += rng.nextInt(); } total.addAndGet(sum); }); } es.shutdown(); es.awaitTermination(10, TimeUnit.SECONDS); System.out.printf("%d in %.3f sec\n", total.get(), (System.currentTimeMillis() - start) * 1e-3); } 6973351531400 in 0.493 sec This is a bit slower but the correct usage of ThreadLocalRandomSource: @Test void test() throws InterruptedException { AtomicLong total = new AtomicLong(); long start = System.currentTimeMillis(); ExecutorService es = Executors.newCachedThreadPool(); for (int i = 0; i < 100; i++) { es.execute(() -> { long sum = 0; for (int j = 0; j < 10000000; j++) { sum += ThreadLocalRandomSource.current(RandomSource.KISS).nextInt(); } total.addAndGet(sum); }); } es.shutdown(); es.awaitTermination(10, TimeUnit.SECONDS); System.out.printf("%d in %.3f sec\n", total.get(), (System.currentTimeMillis() - start) * 1e-3); } -12038673722102 in 1.185 sec Alex --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org For additional commands, e-mail: dev-h...@commons.apache.org