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 (note, it just so happens that the {@link NumberFormat#getInstance} returns (at
040 least for now) a new object every time it is called, which is why it is
041 being used in the example above, else we would have to manually create a new
042 NumberFormat instance instead).
043 */
044 public final class ThreadLocalNumberFormat
045 {
046 public ThreadLocalNumberFormat()
047 { }
048 /*
049 Each get/set into the threadlocal must be seperately by each thread (the
050 initialValue() method is good for auto-assigning a new value but we
051 may need to assign a custom calendar value per thread, so we can't use
052 initialValue()
053 */
054 private final ThreadLocal tlnf = new ThreadLocal();
055
056 public NumberFormat get()
057 {
058 return (NumberFormat) tlnf.get();
059 }
060
061 public void set(NumberFormat nf)
062 {
063 tlnf.set(nf);
064 }
065
066 public boolean isNull()
067 {
068 return tlnf.get() == null;
069 }
070
071 public static void main (String args[]) throws Exception
072 {
073 final ThreadLocalNumberFormat nf1 = new ThreadLocalNumberFormat();
074
075 Thread t1 = new TestThread(false, nf1);
076 Thread t2 = new TestThread(false, nf1);
077 t1.start();
078 t2.start();
079
080 for (int n = 0; n < 100; n++) {
081 new TestThread(true, nf1).start();
082 }
083 }
084
085 static class TestThread extends Thread
086 {
087 boolean timing_only = false;
088 ThreadLocalNumberFormat nf;
089 TestThread(boolean timing_only, ThreadLocalNumberFormat nf) {
090 this.timing_only = timing_only;
091 this.nf = nf;
092 }
093
094 public void run()
095 {
096 //warm-up
097 if (nf.isNull()) {
098 nf.set(NumberFormat.getInstance());
099 }
100
101 Watch w = new NanoWatch();
102 w.start();
103 if (nf.isNull()) {
104 //all threads will have the same instance but thats ok
105 //we are testing the time taken to get a thread local
106 //variable, we don't have to create a new number format
107 //per thread for this example (but we DO for real-world
108 //since these are not thread safe formatters). We use
109 //a thread-specific formatter below (nf)
110 nf.set(NumberFormat.getInstance());
111 }
112 NumberFormat c = nf.get();
113 w.stop();
114
115 NumberFormat nf = NumberFormat.getNumberInstance();
116 nf.setMinimumFractionDigits(2);
117 nf.setMaximumFractionDigits(2);
118 nf.setMinimumIntegerDigits(2);
119
120 if (timing_only)
121 System.out.println("ThreadLocal get: [" + nf.format(w.getTime() / 1000000.00D)
122 + " ms]");
123 else
124 System.out.println("[" + nf.format(w.getTime() / 1000000.00D)
125 + " ms]" + Thread.currentThread()
126 + "/NumberFormat-ID:[" + System.identityHashCode(c)
127 + "] "+ c);
128 }
129 }
130 }