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}