001    // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 
002    // The Molly framework is freely distributable under the terms of an
003    // MIT-style license. For details, see the molly pages web site at:
004    // http://www.mollypages.org/. Use, modify, have fun !
005    
006    package fc.util;
007    
008    import java.util.*;
009    import java.text.*;
010    
011    /** 
012    Useful to store thread-local Random instances.  This class is intended
013    for servlets/molly pages. Instead of static get/set methods, this class
014    must be instantiated and the instance methods used to get/set the random
015    object. This allows multiple instances of this class in the webapp, with
016    each instance being able to get/set a separate random generator. [If the methods in
017    this class were static, then only 1 random could be get/set per thread].
018    <p>
019    <font color=red><b>Each thread must remember to individually create a separate
020    random instance and store it via the set method</b></font>. The usage idiom
021    is:
022    <blockquote>
023    <pre>
024    <font color=blue>
025    //WebApp has a map of ThreadLocalRandoms and also a instance variable
026    //pointing to a default ThreadLocalRandom
027    ThreadLocalRandom myrand = WebApp.getThreadLocalRandom("foo");
028    </font><font color=red>
029    if (myrand.isNull())  {
030      myrand.set(new java.util.Random());
031      }
032    </font><font color=blue>
033    Random rand = myrand.get();
034    </font>
035    </pre>
036    </blockquote>
037    Note, the lines in red are always needed anywhere/anytime this class is used.
038    <p>
039    The methods in java.util.Random are synchronized (or at least they
040    internally use the synchronized <code>next(int)</code> method), so
041    these ThreadLocalRandoms are really only useful in heavily Multi threaded
042    apps where thread contention over a single random number generator
043    can slow things down.
044    */
045    public final class ThreadLocalRandom
046    {
047    public ThreadLocalRandom()
048      { }
049    /*
050    Each get/set into the threadlocal must be seperately by each thread (the
051    initialValue() method is good for auto-assigning a new value but we
052    may need to assign a custom calendar value per thread, so we can't use
053    initialValue()
054    */
055    private final ThreadLocal tlrand = new ThreadLocal();
056    
057    public Random get()
058      {
059      return (Random) tlrand.get();
060      }
061    
062    public void set(Random cal)
063      {
064      tlrand.set(cal);
065      }
066    
067    public boolean isNull()
068      {
069      return tlrand.get() == null;
070      }
071    
072    public static void main (String args[]) throws Exception
073      { 
074      final ThreadLocalRandom cal1 = new ThreadLocalRandom();
075      
076      Thread t1 = new TestThread(false, cal1);
077      Thread t2 = new TestThread(false, cal1);
078      t1.start();
079      t2.start();
080      
081      for (int n = 0; n < 100; n++) {
082        new TestThread(true, cal1).start();
083        }
084      }
085    
086    static class TestThread extends Thread
087      {
088      boolean timing_only = false;
089      ThreadLocalRandom rand;
090      TestThread(boolean timing_only, ThreadLocalRandom rand) {
091        this.timing_only = timing_only;
092        this.rand = rand;
093        }
094        
095      public void run()
096        {
097        //warm up
098        if (rand.isNull()) {
099          rand.set(new java.util.Random());
100          }
101    
102        Watch w = new NanoWatch();
103        w.start();
104        if (rand.isNull()) {
105          rand.set(new java.util.Random());
106          }
107        Random c = rand.get();
108        w.stop();
109        
110        NumberFormat nf = NumberFormat.getNumberInstance();
111        nf.setMinimumFractionDigits(2);
112        nf.setMaximumFractionDigits(2);
113        nf.setMinimumIntegerDigits(2);
114      
115        if (timing_only)
116          System.out.println("[" + nf.format(w.getTime() / 1000000.00D) + " ms]");
117        else
118          System.out.println("[" + nf.format(w.getTime() / 1000000.00D) + " ms]" + Thread.currentThread() + "/Calendar-ID:[" + System.identityHashCode(c) + "] "+ c);
119        }
120      }
121    }