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 number format instances. This class is
013 intended for servlets/molly pages. Instead of static get/set methods,
014 this class must be instantiated and the instance methods used to get/set
015 the calendar object. This allows multiple instances of this class in the
016 webapp, with each instance being able to get/set a separate calendar. [If
017 the methods in this class were static, then only 1 calendar could be
018 get/set per thread].
019 <p>
020 <font color=red><b>Each thread must remember to individually create a
021 separate calendar instance and store it via the set method</b></font>. The
022 usage idiom is:
023 <blockquote>
024 <pre>
025 <font color=blue>
026 //WebApp has a map of ThreadLocalNumberFormat's and also a instance variable
027 //pointing to a default ThreadLocalNumberFormat
028 ThreadLocalNumberFormat mynf = WebApp.getThreadLocalNumberFormat("foo");
029 </font><font color=red>
030 if (mynf.isNull()) {
031 mynf.set(NumberFormat.getInstance());
032 }
033 </font><font color=blue>
034 NumberFormat nf = mynf.get();
035 </font>
036 </pre>
037 </blockquote>
038 Note, the lines in red are always needed anywhere/anytime this class is used.
039 */
040 public final class ThreadLocalNumberFormat
041 {
042 public ThreadLocalNumberFormat()
043 { }
044 /*
045 Each get/set into the threadlocal must be seperately by each thread (the
046 initialValue() method is good for auto-assigning a new value but we
047 may need to assign a custom calendar value per thread, so we can't use
048 initialValue()
049 */
050 private static final ThreadLocal tlnf = new ThreadLocal();
051
052 public NumberFormat get()
053 {
054 return (NumberFormat) tlnf.get();
055 }
056
057 public void set(NumberFormat nf)
058 {
059 tlnf.set(nf);
060 }
061
062 public boolean isNull()
063 {
064 return tlnf.get() == null;
065 }
066
067 public static void main (String args[]) throws Exception
068 {
069 final ThreadLocalNumberFormat nf1 = new ThreadLocalNumberFormat();
070
071 Thread t1 = new TestThread(false, nf1);
072 Thread t2 = new TestThread(false, nf1);
073 t1.start();
074 t2.start();
075
076 for (int n = 0; n < 100; n++) {
077 new TestThread(true, nf1).start();
078 }
079 }
080
081 static class TestThread extends Thread
082 {
083 boolean timing_only = false;
084 ThreadLocalNumberFormat nf;
085 TestThread(boolean timing_only, ThreadLocalNumberFormat nf) {
086 this.timing_only = timing_only;
087 this.nf = nf;
088 }
089
090 public void run()
091 {
092 //warm-up
093 if (nf.isNull()) {
094 nf.set(NumberFormat.getInstance());
095 }
096
097 Watch w = new NanoWatch();
098 w.start();
099 if (nf.isNull()) {
100 //all threads will have the same instance but thats ok
101 //we are testing the time taken to get a thread local
102 //variable, we don't have to create a new number format
103 //per thread for this example (but we DO for real-world
104 //since these are not thread safe formatters). We use
105 //a thread-specific formatter below (nf)
106 nf.set(NumberFormat.getInstance());
107 }
108 NumberFormat c = nf.get();
109 w.stop();
110
111 NumberFormat nf = NumberFormat.getNumberInstance();
112 nf.setMinimumFractionDigits(2);
113 nf.setMaximumFractionDigits(2);
114 nf.setMinimumIntegerDigits(2);
115
116 if (timing_only)
117 System.out.println("ThreadLocal get: [" + nf.format(w.getTime() / 1000000.00D)
118 + " ms]");
119 else
120 System.out.println("[" + nf.format(w.getTime() / 1000000.00D)
121 + " ms]" + Thread.currentThread()
122 + "/NumberFormat-ID:[" + System.identityHashCode(c)
123 + "] "+ c);
124 }
125 }
126 }