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 }