ThreadLocalRandomSource.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.rng.simple;

  18. import java.util.EnumMap;
  19. import java.util.Map;
  20. import org.apache.commons.rng.UniformRandomProvider;

  21. /**
  22.  * This class provides a thread-local {@link UniformRandomProvider}.
  23.  *
  24.  * <p>The {@link UniformRandomProvider} is created once-per-thread using the default
  25.  * construction method {@link RandomSource#create()}.
  26.  *
  27.  * <p>Example:</p>
  28.  * <pre><code>
  29.  * import org.apache.commons.rng.simple.RandomSource;
  30.  * import org.apache.commons.rng.simple.ThreadLocalRandomSource;
  31.  * import org.apache.commons.rng.sampling.distribution.PoissonSampler;
  32.  *
  33.  * // Access a thread-safe random number generator
  34.  * UniformRandomProvider rng = ThreadLocalRandomSource.current(RandomSource.SPLIT_MIX_64);
  35.  *
  36.  * // One-time Poisson sample
  37.  * double mean = 12.3;
  38.  * int counts = PoissonSampler.of(rng, mean).sample();
  39.  * </code></pre>
  40.  *
  41.  * <p>Note if the {@link RandomSource} requires additional arguments then it is not
  42.  * supported. The same can be achieved using:</p>
  43.  *
  44.  * <pre><code>
  45.  * import org.apache.commons.rng.simple.RandomSource;
  46.  * import org.apache.commons.rng.sampling.distribution.PoissonSampler;
  47.  *
  48.  * // Provide a thread-safe random number generator with data arguments
  49.  * private static ThreadLocal&lt;UniformRandomProvider&gt; rng =
  50.  *     new ThreadLocal&lt;UniformRandomProvider&gt;() {
  51.  *         &#64;Override
  52.  *         protected UniformRandomProvider initialValue() {
  53.  *             return RandomSource.TWO_CMRES_SELECT.create(null, 3, 4);
  54.  *         }
  55.  *     };
  56.  *
  57.  * // One-time Poisson sample using a thread-safe random number generator
  58.  * double mean = 12.3;
  59.  * int counts = PoissonSampler.of(rng.get(), mean).sample();
  60.  * </code></pre>
  61.  *
  62.  * @since 1.3
  63.  */
  64. public final class ThreadLocalRandomSource {
  65.     /**
  66.      * A map containing the {@link ThreadLocal} instance for each {@link RandomSource}.
  67.      *
  68.      * <p>This should only be modified to create new instances in a synchronized block.
  69.      */
  70.     private static final Map<RandomSource, ThreadLocal<UniformRandomProvider>> SOURCES =
  71.         new EnumMap<>(RandomSource.class);

  72.     /** No public construction. */
  73.     private ThreadLocalRandomSource() {}

  74.     /**
  75.      * Extend the {@link ThreadLocal} to allow creation of the desired {@link RandomSource}.
  76.      */
  77.     private static class ThreadLocalRng extends ThreadLocal<UniformRandomProvider> {
  78.         /** The source. */
  79.         private final RandomSource source;

  80.         /**
  81.          * Create a new instance.
  82.          *
  83.          * @param source the source
  84.          */
  85.         ThreadLocalRng(RandomSource source) {
  86.             this.source = source;
  87.         }

  88.         @Override
  89.         protected UniformRandomProvider initialValue() {
  90.             // Create with the default seed generation method
  91.             return source.create();
  92.         }
  93.     }

  94.     /**
  95.      * Returns the current thread's copy of the given {@code source}. If there is no
  96.      * value for the current thread, it is first initialized to the value returned
  97.      * by {@link RandomSource#create()}.
  98.      *
  99.      * <p>Note if the {@code source} requires additional arguments then it is not
  100.      * supported.
  101.      *
  102.      * @param source the source
  103.      * @return the current thread's value of the {@code source}.
  104.      * @throws IllegalArgumentException if the source is null or the source requires arguments
  105.      */
  106.     public static UniformRandomProvider current(RandomSource source) {
  107.         ThreadLocal<UniformRandomProvider> rng = SOURCES.get(source);
  108.         // Implement double-checked locking:
  109.         // https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
  110.         if (rng == null) {
  111.             // Do the checks on the source here since it is an edge case
  112.             // and the EnumMap handles null (returning null).
  113.             if (source == null) {
  114.                 throw new IllegalArgumentException("Random source is null");
  115.             }

  116.             synchronized (SOURCES) {
  117.                 rng = SOURCES.computeIfAbsent(source, ThreadLocalRng::new);
  118.             }
  119.         }
  120.         return rng.get();
  121.     }
  122. }