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 number format instances.  This class is
013intended for servlets/molly pages. Instead of static get/set methods,
014this class must be instantiated and the instance methods used to get/set
015the calendar object. This allows multiple instances of this class in the
016webapp, with each instance being able to get/set a separate calendar. [If
017the methods in this class were static, then only 1 calendar could be
018get/set per thread].
019<p>
020<font color=red><b>Each thread must remember to individually create a
021separate calendar instance and store it via the set method</b></font>. The
022usage 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
028ThreadLocalNumberFormat mynf = WebApp.getThreadLocalNumberFormat("foo");
029</font><font color=red>
030if (mynf.isNull())  {
031  mynf.set(NumberFormat.getInstance());
032  }
033</font><font color=blue>
034NumberFormat nf = mynf.get();
035</font>
036</pre>
037</blockquote>
038Note, 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
040least for now) a new object every time it is called, which is why it is
041being used in the example above, else we would have to manually create a new
042NumberFormat instance instead).
043*/
044public final class ThreadLocalNumberFormat
045{
046public ThreadLocalNumberFormat()
047  { }
048/*
049Each get/set into the threadlocal must be seperately by each thread (the
050initialValue() method is good for auto-assigning a new value but we
051may need to assign a custom calendar value per thread, so we can't use
052initialValue()
053*/
054private final ThreadLocal tlnf = new ThreadLocal();
055
056public NumberFormat get()
057  {
058  return (NumberFormat) tlnf.get();
059  }
060
061public void set(NumberFormat nf)
062  {
063  tlnf.set(nf);
064  }
065
066public boolean isNull()
067  {
068  return tlnf.get() == null;
069  }
070
071public 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
085static 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}