diff --git a/src/main/java/com/samskivert/util/Randoms.java b/src/main/java/com/samskivert/util/Randoms.java index 24956414..c16e9d2e 100644 --- a/src/main/java/com/samskivert/util/Randoms.java +++ b/src/main/java/com/samskivert/util/Randoms.java @@ -46,6 +46,25 @@ public class Randoms return new Randoms(rand); } + /** + * Get a thread-local Randoms instance that will not contend with any other thread + * for random number generation. + * + *
Note: while all Randoms instances are thread-safe, normally they use a + * java.util.Random internally that must protect against multiple threads generating + * psuedorandom numbers with it simultaneously. This method will return a Randoms + * that uses an internal Random subclass with no such safeguards, resulting in much + * less overhead. However, you should probably not store a reference to the result, + * but instead always use it immediately as in the following example: + *
+ * Puppy pick = Randoms.threadLocal().pick(Puppy.LITTER, null); + *+ */ + public static Randoms threadLocal () + { + return _localRandoms.get(); + } + /** * Returns a pseudorandom, uniformly distributed
int value between 0 (inclusive)
* and the specified value (exclusive).
@@ -261,4 +280,188 @@ public class Randoms
/** The random number generator. */
protected final Random _r;
+
+ /** A ThreadLocal for accessing a thread-local version of Randoms. */
+ protected static final ThreadLocalUsages of this class should typically be of the form: + * {@code ThreadLocalRandom.current().nextX(...)} (where + * {@code X} is {@code Int}, {@code Long}, etc). + * When all usages are of this form, it is never possible to + * accidently share a {@code ThreadLocalRandom} across multiple threads. + * + *
This class also provides additional commonly used bounded random + * generation methods. + * + * @since 1.7 + * @author Doug Lea + */ + static class ThreadLocalRandom extends Random { + // same constants as Random, but must be redeclared because private + private final static long multiplier = 0x5DEECE66DL; + private final static long addend = 0xBL; + private final static long mask = (1L << 48) - 1; + + /** + * The random seed. We can't use super.seed. + */ + private long rnd; + + /** + * Initialization flag to permit calls to setSeed to succeed only + * while executing the Random constructor. We can't allow others + * since it would cause setting seed in one part of a program to + * unintentionally impact other usages by the thread. + */ + boolean initialized; + + // Padding to help avoid memory contention among seed updates in + // different TLRs in the common case that they are located near + // each other. + private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7; + + /** + * Constructor called only by localRandom.initialValue. + */ + ThreadLocalRandom() { + super(); + initialized = true; + } + + /** + * Throws {@code UnsupportedOperationException}. Setting seeds in + * this generator is not supported. + * + * @throws UnsupportedOperationException always + */ + public void setSeed(long seed) { + if (initialized) + throw new UnsupportedOperationException(); + rnd = (seed ^ multiplier) & mask; + } + + protected int next(int bits) { + rnd = (rnd * multiplier + addend) & mask; + return (int) (rnd >>> (48-bits)); + } + +// as of JDK 1.6, this method does not exist in java.util.Random +// /** +// * Returns a pseudorandom, uniformly distributed value between the +// * given least value (inclusive) and bound (exclusive). +// * +// * @param least the least value returned +// * @param bound the upper bound (exclusive) +// * @throws IllegalArgumentException if least greater than or equal +// * to bound +// * @return the next value +// */ +// public int nextInt(int least, int bound) { +// if (least >= bound) +// throw new IllegalArgumentException(); +// return nextInt(bound - least) + least; +// } + + /** + * Returns a pseudorandom, uniformly distributed value + * between 0 (inclusive) and the specified value (exclusive). + * + * @param n the bound on the random number to be returned. Must be + * positive. + * @return the next value + * @throws IllegalArgumentException if n is not positive + */ + public long nextLong(long n) { + if (n <= 0) + throw new IllegalArgumentException("n must be positive"); + // Divide n by two until small enough for nextInt. On each + // iteration (at most 31 of them but usually much less), + // randomly choose both whether to include high bit in result + // (offset) and whether to continue with the lower vs upper + // half (which makes a difference only if odd). + long offset = 0; + while (n >= Integer.MAX_VALUE) { + int bits = next(2); + long half = n >>> 1; + long nextn = ((bits & 2) == 0) ? half : n - half; + if ((bits & 1) == 0) + offset += n - nextn; + n = nextn; + } + return offset + nextInt((int) n); + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @return the next value + * @throws IllegalArgumentException if least greater than or equal + * to bound + */ + public long nextLong(long least, long bound) { + if (least >= bound) + throw new IllegalArgumentException(); + return nextLong(bound - least) + least; + } + + /** + * Returns a pseudorandom, uniformly distributed {@code double} value + * between 0 (inclusive) and the specified value (exclusive). + * + * @param n the bound on the random number to be returned. Must be + * positive. + * @return the next value + * @throws IllegalArgumentException if n is not positive + */ + public double nextDouble(double n) { + if (n <= 0) + throw new IllegalArgumentException("n must be positive"); + return nextDouble() * n; + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @return the next value + * @throws IllegalArgumentException if least greater than or equal + * to bound + */ + public double nextDouble(double least, double bound) { + if (least >= bound) + throw new IllegalArgumentException(); + return nextDouble() * (bound - least) + least; + } + + private static final long serialVersionUID = -5851777807851030925L; + } }