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}