On 08/22/2013 03:37 AM, Brian Burkhalter wrote:
> With respect to this issue
> 
> http://bugs.sun.com/view_bug.do?bug_id=6470700
> 
> the code of concern from java.lang.Math is
> 
> 701    private static Random randomNumberGenerator;
> 702
> 703    private static synchronized Random initRNG() {
> 704        Random rnd = randomNumberGenerator;
> 705        return (rnd == null) ? (randomNumberGenerator = new Random()) : 
> rnd;
> 706     }
> 
> 731    public static double random() {
> 732        Random rnd = randomNumberGenerator;
> 733        if (rnd == null) rnd = initRNG();
> 734        return rnd.nextDouble();
> 735    }
> 
> where the class variable "randomNumberGenerator" is not used anywhere else in 
> the class. The complaint is that this is an instance of the broken 
> double-checked locking pattern. While at first glance this might appear to be 
> the case, it does not seem so to me. It looks more like an attempt to avoid 
> calling a synchronized method if "randomNumberGenerator" has already been 
> initialized.
> 
> A more typical pattern would be
> 
>     private static Random randomNumberGenerator;
> 
>     private static synchronized Random getRNG() {
>         if (randomNumberGenerator == null) {
>             randomNumberGenerator = new Random();
>         }
>         return randomNumberGenerator;
>     }
> 
>     public static double random() {
>         return getRNG().nextDouble();
>     }
> 
> Comments, please.

(Sorry, I'm late to the party!)

I'm surprised people are still haunted by DCL :) In the correct form, it
has generally negligible synchronization overheads associated with
volatile read, and in return you have no class metadata waste. As in:

  private static volatile Random rng;

  private static Random getRNG() {
    if (rng == null) {
       synchronized(Math.class) {
          rng = new Random();
       }
    }
    return rng;
  }

  public static double random() {
    return getRNG().nextDouble();
  }

Also, random() will probably be the scalability bottleneck anyway due to
j.l.Random internal atomics. That means, we do not have to provide the
best possible performance for getRNG(), but rather eliminate the
scalability bottleneck. Holder idiom has a benefit of being sequentially
faster (because JITs elide the class init checks, and read the static
field), but in this case of heavy-weight mechanics in j.u.Random, it is
irrelevant. Hence, we can minimize the downside for class metadata
growth by going with DCL.

I would think the *major* problem the patch should fix is the visibility
of new Random() state in the other threads, because its instance is
being published via the data race -- both correct DCL and Holder idiom
solve that. But then again, our current implementation for Random is
using AtomicLong, which has the inherent thread-safeness, making the
only erroneous scenario impossible.

Bottom-line: commit either correct DCL "just" to secure ourselves from
the adversarial j.u.Random changes.

-Aleksey.

Reply via email to