/* Quick test of verious singleton implementations last update: Sat Mar 24 12:35:39 2001 Doug Lea (dl at gee) */ class ThreadSpecificSingleton implements Runnable { static abstract class Singleton { // a field and method to prevent some compiler optimizations int aField = System.identityHashCode(this); int aMethod(int i) { return (i % 17) != 0 ? aField: i; } } static class EagerSingleton extends Singleton { static final EagerSingleton theInstance = new EagerSingleton(); static EagerSingleton getInstance() { return theInstance; } } static class TSSingleton extends Singleton { final static ThreadLocal perThreadInstance = new ThreadLocal(); static TSSingleton theInstance; static TSSingleton getInstance() { TSSingleton instance = (TSSingleton)(perThreadInstance.get()); if (instance == null) { // Might as well use the ThreadLocal itself as the lock synchronized(perThreadInstance) { instance = theInstance; if (instance == null) instance = theInstance = new TSSingleton(); } // copy global to per-thread perThreadInstance.set(instance); } return instance; } } static class SynchedSingleton extends Singleton { static SynchedSingleton theInstance; static synchronized SynchedSingleton getInstance() { if (theInstance == null) theInstance = new SynchedSingleton(); return theInstance; } } static class VolatileSingleton extends Singleton { final static Object lock = new Object(); static volatile VolatileSingleton theInstance; static VolatileSingleton getInstance() { VolatileSingleton instance = theInstance; if (instance == null) { synchronized(lock) { instance = theInstance; if (instance == null) instance = theInstance = new VolatileSingleton(); } } return instance; } } static final int ITERS = 1000000; static final int NTHREADS = 8; static int total; // accumulate calls to aMethod, to prevent overoptimizing final int mode; ThreadSpecificSingleton(int md) { mode = md; } public void run() { int sum = 0; // to prevent optimizations for (int i = 0; i < ITERS; ++i) { if (mode == 0) sum += EagerSingleton.getInstance().aMethod(i); else if (mode == 1) sum += TSSingleton.getInstance().aMethod(i); else if (mode == 2) sum += VolatileSingleton.getInstance().aMethod(i); else if (mode == 3) sum += SynchedSingleton.getInstance().aMethod(i); } total += sum; } public static void main(String[] args) { Thread[] threads = new Thread[NTHREADS]; for (int reps = 0; reps < 3; ++reps) { for (int mode = 0; mode < 4; ++mode) { if (mode == 0) System.out.print("Eager: "); else if (mode == 1) System.out.print("TSS: "); else if (mode == 2) System.out.print("Volatile: "); else if (mode == 3) System.out.print("Synch: "); long startTime = System.currentTimeMillis(); for (int i = 0; i < NTHREADS; ++i) threads[i] = new Thread(new ThreadSpecificSingleton(mode)); for (int i = 0; i < NTHREADS; ++i) threads[i].start(); try { for (int i = 0; i < NTHREADS; ++i) threads[i].join(); } catch (InterruptedException ie) { System.out.println("Interrupted"); return; } long elapsed = System.currentTimeMillis() - startTime; System.out.println(elapsed + "ms"); if (total == 0) // ensure total is live to avoid optimizing away System.out.println("useless number = " + total); } } } }