tags:

views:

571

answers:

6

The classic of writing a singleton in java is like this:

public class SingletonObject
{
    private SingletonObject()
    {
    }

    public static SingletonObject getSingletonObject()
    {
      if (ref == null)
          // it's ok, we can call this constructor
          ref = new SingletonObject();
      return ref;
    }

    private static SingletonObject ref;
}

and we can add synchronized keyword if we need it to run in multithreaded cases.

But I prefer to write it as:

public class SingletonObject
{
    private SingletonObject()
    {
        // no code req'd
    }

    public static SingletonObject getSingletonObject()
    {
      return ref;
    }

    private static SingletonObject ref = new SingletonObject();
}

which I think is more concise, but strangely I didn't see any sample code written in this way, is there any bad effects if I wrote my code in this way?

+3  A: 

The difference between your code and the "sample code" is that your singleton is instantiated when the class is loaded, while in the "sample" version, it is not instantiated until it is actually needed.

Anon.
you explain this problem clearly
gleery
Of course the difference is usually irrelevant since java classes are loaded on demand.
Michael Borgwardt
+3  A: 

Well, in the latter case the singleton object gets created before it is ever needed, but in most cases that's probably not horribly bad.

By the way, Joshua Bloch recommends (in Effective Java, 2nd ed, item 4) implementing singletons using a single-element enum:

public enum SingletonObject { 
    INSTANCE;
}

He gives the following justification:

[...] it is more concise, provides serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks. While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton.

Jonik
Rats, there's a second edition ? (badabing)
Steve De Caux
I read a few chapters of this book a long time ago, maybe I should read carefully this time
gleery
I'm not sure it's meaningful to provide " an ironclad guarantee against multiple instantiation, even in the face of ... reflection attacks". If an attacker is in a position to run a reflection attack, they completely own the VM and there's not really anything you can do about it. It's like quadruple-locking your front door and putting a sophisticated alarm system on it, while leaving the windows wide open.
Anon.
@Steve: Yeah, it came out in 2008. Definitely a worthwhile (and much needed) update, even if the best parts of it remain largely unchanged from the 1st ed
Jonik
Anon: the enum is just plain easier to write and read anyway, though.
Kevin Bourrillion
+3  A: 

I would say the latter code is the more standard pattern, actually. Your first version isn't thread-safe. Ways of making it thread-safe include synchronizing on every access, or very carefully making it use double-checked locking (which is safe as of the Java 5 memory model, so long as you get it right).

Note that due to classes being initialized lazily, your latter code would still only create an object unnecessarily if you called static methods on the class without wanting to create the instance.

There's a pattern using a nested class to do the initialization which can make this lazier, but personally the second form almost always does well enough for me on its own.

There are more details of this in Effective Java, but I don't have it with me to find the item number, I'm afraid.

Jon Skeet
+1 for hinting at issues with Double Checked Locking. -1 for missing the opportunity of pointing out the dangers of using singletons in the first place ;-)
Phil Nash
thank you, really good answer
gleery
+1  A: 

I think your problem is that you're mixing singleton and lazy initialization. A singleton can be implemented with different initialization strategies:

  • initialization on class loading
  • lazy initialization that uses double checked locking
  • lazy initialization with single checking (with possible repeated initialization)
  • lazy initialization that uses the class loader (holder class idiom)

All these approaches are discussed in Effective Java 2nd Item 71: Use lazy initialization judiciously.

Thomas Jung
I'll read it later,:)
gleery
A: 

I agree with Anon, and in the case where I always want to instantiate the singleton I would use

public class SingletonObject
{
public static SingletonObject REF = new SingletonObject();

private SingletonObject()
{
    // no code req'd
}

}

Steve De Caux
A: 

In the second form, your singleton is eagerly loaded and this is actually the preferred form (and the first one isn't thread-safe as you mentioned it yourself). Eager loading is not a bad thing for production code but there are contexts where you might want to lazy load your singletons, as discussed by the author of Guice, Bob Lee, in Lazy Loading Singletons that I'm quoting below:

First, why would you want to lazy load a singleton? In production, you typically want to eagerly load all your singletons so you catch errors early and take any performance hit up front, but in tests and during development, you only want to load what you absolutely need so as not to waste time.

Before Java 1.5, I lazy loaded singletons using plain old synchronization, simple but effective:

static Singleton instance;

public static synchronized Singleton getInstance() {
  if (instance == null)
    instance == new Singleton();
  return instance;
}

Changes to the memory model in 1.5 enabled the infamous Double-Checked Locking (DCL) idiom. To implement DCL, you check a volatile field in the common path and only synchronize when necessary:

static volatile Singleton instance;

public static Singleton getInstance() {
  if (instance == null) {
    synchronized (Singleton.class) {
      if (instance == null)
        instance == new Singleton();
    }
  }
  return instance;
}

But volatile isn't that much faster than synchronized, synchronized is pretty fast nowadays, and DCL requires more code, so even after 1.5 came out, I continued using plain old synchronization.

Imagine my surprise today when Jeremy Manson pointed me to the Initialization on Demand Holder (IODH) idiom which requires very little code and has zero synchronization overhead. Zero, as in even faster than volatile. IODH requires the same number of lines of code as plain old synchronization, and it's faster than DCL!

IODH utilizes lazy class initialization. The JVM won't execute a class's static initializer until you actually touch something in the class. This applies to static nested classes, too. In the following example, the JLS guarantees the JVM will not initialize instance until someone calls getInstance():

static class SingletonHolder {
  static Singleton instance = new Singleton();    
}

public static Singleton getInstance() {
  return SingletonHolder.instance;
}

[...]

Update: Credit where credit is due, Effective Java (copyright 2001) detailed this pattern under item 48. It goes on to point out that you still have to use synchronization or DCL in non-static contexts.

I also switched singleton handling in my framework from synchronization to DCL and saw another 10% performance boost (compared to before I started using cglib's fast reflection). I only used one thread in my micro-benchmark, so the boost to concurrency could be even greater given that I replaced a heavily contended lock with a relatively fine grained volatile field access.

Note that Joshua Bloch now recommends (since Effective Java, 2nd ed) to implement singletons using a single-element enum as pointed out by Jonik.

Pascal Thivent