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

Reply via email to