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
006package fc.util;
007
008import java.util.*;
009import java.text.*;
010
011/** 
012Useful to store thread-local Random instances.  This class is intended
013for servlets/molly pages. Instead of static get/set methods, this class
014must be instantiated and the instance methods used to get/set the random
015object. This allows multiple instances of this class in the webapp, with
016each instance being able to get/set a separate random generator. [If the methods in
017this 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
020random instance and store it via the set method</b></font>. The usage idiom
021is:
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
027ThreadLocalRandom myrand = WebApp.getThreadLocalRandom("foo");
028</font><font color=red>
029if (myrand.isNull())  {
030  myrand.set(new java.util.Random());
031  }
032</font><font color=blue>
033Random rand = myrand.get();
034</font>
035</pre>
036</blockquote>
037Note, the lines in red are always needed anywhere/anytime this class is used.
038<p>
039The methods in java.util.Random are synchronized (or at least they
040internally use the synchronized <code>next(int)</code> method), so
041these ThreadLocalRandoms are really only useful in heavily Multi threaded
042apps where thread contention over a single random number generator
043can slow things down.
044*/
045public final class ThreadLocalRandom
046{
047public ThreadLocalRandom()
048  { }
049/*
050Each get/set into the threadlocal must be seperately by each thread (the
051initialValue() method is good for auto-assigning a new value but we
052may need to assign a custom calendar value per thread, so we can't use
053initialValue()
054*/
055private final ThreadLocal tlrand = new ThreadLocal();
056
057public Random get()
058  {
059  return (Random) tlrand.get();
060  }
061
062public void set(Random cal)
063  {
064  tlrand.set(cal);
065  }
066
067public boolean isNull()
068  {
069  return tlrand.get() == null;
070  }
071
072public 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
086static 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}